<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta name="renderer" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <link rel="dns-prefetch" href="http://example.com">
  <title>Yuan H.N</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="C++、计算机三维视觉、机器人">
<meta property="og:type" content="website">
<meta property="og:title" content="Yuan H.N">
<meta property="og:url" content="http://example.com/index.html">
<meta property="og:site_name" content="Yuan H.N">
<meta property="og:description" content="C++、计算机三维视觉、机器人">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="Henan Yuan">
<meta property="article:tag" content="C++ 计算机视觉 三维视觉 Opencv">
<meta name="twitter:card" content="summary">
  
    <link rel="alternative" href="/atom.xml" title="Yuan H.N" type="application/atom+xml">
  
  
    <link rel="icon" href="/assets/icon.png">
  
  <link rel="stylesheet" type="text/css" href="/./main.0cf68a.css">
  <style type="text/css">
  
    #container.show {
      background: linear-gradient(200deg,#a0cfe4,#e8c37e);
    }
  </style>
  

  

<meta name="generator" content="Hexo 6.0.0"></head>

<body>
  <div id="container" q-class="show:isCtnShow">
    <canvas id="anm-canvas" class="anm-canvas"></canvas>
    <div class="left-col" q-class="show:isShow">
      
<div class="overlay" style="background: #4d4d4d"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			<img src="/assets/touxiang.png" class="js-avatar">
		</a>
		<hgroup>
		  <h1 class="header-author"><a href="/"></a></h1>
		</hgroup>
		

		<nav class="header-menu">
			<ul>
			
				<li><a href="/">主页</a></li>
	        
			</ul>
		</nav>
		<nav class="header-smart-menu">
    		
    			
    			<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
    			
            
    			
    			<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
    			
            
		</nav>
		<nav class="header-nav">
			<div class="social">
				
					<a class="weibo" target="_blank" href="#" title="weibo"><i class="icon-weibo"></i></a>
		        
					<a class="qq" target="_blank" href="#" title="qq"><i class="icon-qq"></i></a>
		        
					<a class="weixin" target="_blank" href="#" title="weixin"><i class="icon-weixin"></i></a>
		        
					<a class="bilibili" target="_blank" href="#" title="bilibili"><i class="icon-bilibili"></i></a>
		        
			</div>
		</nav>
	</header>		
</div>

    </div>
    <div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
      
<nav id="mobile-nav">
  	<div class="overlay js-overlay" style="background: #4d4d4d"></div>
	<div class="btnctn js-mobile-btnctn">
  		<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
				<img src="/assets/touxiang.png" class="js-avatar">
			</div>
			<hgroup>
			  <h1 class="header-author js-header-author"></h1>
			</hgroup>
			
			
			
				
			
			
			
			<nav class="header-nav">
				<div class="social">
					
						<a class="weibo" target="_blank" href="#" title="weibo"><i class="icon-weibo"></i></a>
			        
						<a class="qq" target="_blank" href="#" title="qq"><i class="icon-qq"></i></a>
			        
						<a class="weixin" target="_blank" href="#" title="weixin"><i class="icon-weixin"></i></a>
			        
						<a class="bilibili" target="_blank" href="#" title="bilibili"><i class="icon-bilibili"></i></a>
			        
				</div>
			</nav>

			<nav class="header-menu js-header-menu">
				<ul style="width: 50%">
				
				
					<li style="width: 100%"><a href="/">主页</a></li>
		        
				</ul>
			</nav>
		</header>				
	</div>
	<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>

      <div id="wrapper" class="body-wrap">
        <div class="menu-l">
          <div class="canvas-wrap">
            <canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1" class="anm-canvas"></canvas>
          </div>
          <div id="js-content" class="content-ll">
            
  
    <article id="post-boost使用教程——shared-ptr" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/03/30/boost%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%E2%80%94%E2%80%94shared-ptr/">boost使用教程——shared_ptr</a>
    </h1>
  

        
        <a href="/2022/03/30/boost%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%E2%80%94%E2%80%94shared-ptr/" class="archive-article-date">
  	<time datetime="2022-03-30T13:17:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-03-30</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="1-Boost库使用教程——share-ptr"><a href="#1-Boost库使用教程——share-ptr" class="headerlink" title="1 Boost库使用教程——share_ptr"></a>1 Boost库使用教程——<code>share_ptr</code></h3><p>头文件: <code>&quot;boost/shared_ptr.hpp&quot;</code></p>
<h4 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h4><p>​        <code>shared_ptr</code> 可以从一个裸指针、另一个 shared_ptr、一个<code> std::auto_ptr</code>、或者一个<code> boost::weak_ptr</code> 构造。还可以传递第二个参数给 <code>shared_ptr</code> 的构造函数，它被称为删除器(deleter)。删除器稍后会被调用，来处理共享资源的释放。这对于管理那些不是用 new 分配也不是用 <code>delete</code> 释放的资源时非常有用(稍后将看到创建客户化删除器的例子)。 <code>shared_ptr</code> 被创建后，它就可象普通指针一样使用了，除了一点，<strong>它不能被显式地删除。</strong></p>
<p>​        引用计数智能指针可以分为插入式(intrusive)和非插入式(non-intrusive)两 类。前者要求它所管理 的类提供明确的函数或数据成员用于管理引用计数。这意味着在类的设计时就必须预见到它将与一个插入式的引用计数智能指针一起工作，或 者重新设计它。非插入式的引用计数智能指针对它所管理的类没有任何要求。引用计数智 能指针拥有与它所存指针有关的内存的所有权。没有智能指针的帮助，对象 的共享会存在问题，必须有人负负责删除共享的内存。</p>
<p>​        <code>shared_ptr</code> 的源代码部分摘要，重要的成员函数和成员变量已经被列出：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> boost &#123;</span><br><span class="line">     <span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt; <span class="class"><span class="keyword">class</span> <span class="title">shared_ptr</span> &#123;</span></span><br><span class="line">     <span class="keyword">public</span>:</span><br><span class="line">         <span class="comment">// 构造函数</span></span><br><span class="line">         <span class="comment">//这个构造函数获得给定指针 p 的所有权。参数 p 必须是指向 Y 的有效指针。构造后引用计数设为1。唯一从这个构造函数抛出的异常是 			std::bad_alloc (仅在一种很罕见的情况下发生，即不能获得引用计数器所需的自由空间)。(内存分配失败)</span></span><br><span class="line">         <span class="keyword">template</span> &lt;<span class="class"><span class="keyword">class</span> <span class="title">Y</span>&gt;</span> <span class="function"><span class="keyword">explicit</span> <span class="title">shared_ptr</span><span class="params">(Y* p)</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">//这个构造函数带有两个参数。第一个是 shared_ptr 将要获得所有权的那个资源，第二个是 shared_ptr 被销毁时负责释放资源的一</span></span><br><span class="line">         <span class="comment">//个对象，被保存的资源将以 d(p) 的形式传给那个对象。因此 p 的值是否有效取决于 d。如果引用计数器不能分配成功，shared_ptr</span></span><br><span class="line">         <span class="comment">//抛出一个类型为 std::bad_alloc 的异常</span></span><br><span class="line">         <span class="keyword">template</span> &lt;<span class="class"><span class="keyword">class</span> <span class="title">Y</span>,<span class="keyword">class</span> <span class="title">D</span>&gt;</span> <span class="built_in">shared_ptr</span>(Y* p, D d);</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 析构函数 shared_ptr 析构函数对引用计数减一。如果计数为零，则保存的指针被删除。删除指针的方法是调用 operator delete</span></span><br><span class="line">         <span class="comment">// 或者，如果给定了一个执行删除操作的客户化删除器对象，就把保存的指针作为唯一参数调用这个对象。析构函数不会抛出异常。</span></span><br><span class="line">         ~<span class="built_in">shared_ptr</span>();</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 拷贝构造函数, r 中保存的资源被新构造的 shared_ptr 所共享，引用计数加一。这个构造函数不会抛出异常。</span></span><br><span class="line">         <span class="built_in">shared_ptr</span>(<span class="keyword">const</span> shared_ptr &amp; r);</span><br><span class="line">         </span><br><span class="line">         <span class="comment">//  weak_ptr (本章稍后会介绍)构造 shared_ptr。这使得 weak_ptr 的使用具有线程安全性，因为指向</span></span><br><span class="line">         <span class="comment">// weak_ptr 参数的共享资源的引用计数将会自增(weak_ptr 不影响共享资源的引用计数)。如果 weak_ptr 为空 </span></span><br><span class="line">         <span class="comment">// (r.use_count()==0), shared_ptr 抛出一个类型为 bad_weak_ptr 的异常。</span></span><br><span class="line">         <span class="keyword">template</span> &lt;<span class="class"><span class="keyword">class</span> <span class="title">Y</span>&gt;</span> <span class="function"><span class="keyword">explicit</span> <span class="title">shared_ptr</span><span class="params">(<span class="keyword">const</span> weak_ptr&lt;Y&gt;&amp; r)</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 构造函数 这个构造函数从一个 auto_ptr 获取 r 中保存的指针的所有权，方法是保存指针的一份拷贝并对 auto_ptr</span></span><br><span class="line">         <span class="comment">// 调用release 。构造后的引用计数为1。而 r 当然就变为空的。如果引用计数器不能分配成功，</span></span><br><span class="line">         <span class="comment">// 则抛出 std::bad_alloc 。</span></span><br><span class="line">         <span class="keyword">template</span> &lt;<span class="class"><span class="keyword">class</span> <span class="title">Y</span>&gt;</span> <span class="function"><span class="keyword">explicit</span> <span class="title">shared_ptr</span><span class="params">(std::auto_ptr&lt;Y&gt;&amp; r)</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">//赋值操作共享 r 中的资源，并停止对原有资源的共享。赋值操作不会抛出异常。</span></span><br><span class="line">         shared_ptr&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> shared_ptr&amp; r);</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 成员函数 reset 函数用于停止对保存指针的所有权的共享。共享资源的引用计数减一</span></span><br><span class="line">         <span class="function"><span class="keyword">void</span> <span class="title">reset</span><span class="params">()</span></span>; </span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 操作符重载这个操作符返回对已存指针所指向的对象的一个引用。如果指针为空，调用 operator* 会导致未定义行为。</span></span><br><span class="line">         <span class="comment">// 这个操作符不会抛出异常。</span></span><br><span class="line">         T&amp; <span class="keyword">operator</span>*() <span class="keyword">const</span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 这个操作符返回保存的指针。这个操作符与 operator* 一起使得智能指针看起来象普通指针。这个操作符不会抛出异常</span></span><br><span class="line">         T* <span class="keyword">operator</span>-&gt;() <span class="keyword">const</span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 成员函数 get 函数是当保存的指针有可能为空时(这时 operator* 和 operator-&gt; 都会导致未定义行为)获取它的最好办法。</span></span><br><span class="line">         <span class="comment">// 注意，你也可以使用隐式布尔类型转换来测试 shared_ptr 是否包含有效指针。这个函数不会抛出异常</span></span><br><span class="line">         <span class="function">T* <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 这个函数在 shared_ptr 是它所保存指针的唯一拥有者时返回 true；否则返回 false。 unique 不会抛出异常。</span></span><br><span class="line">         <span class="function"><span class="keyword">bool</span> <span class="title">unique</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">//use_count 函数返回指针的引用计数。它在调试的时候特别有用，因为它可以在程序执行的关键点获得引用计数的快照。</span></span><br><span class="line">         <span class="comment">// 小心地使用它，因为在某些可能的 shared_ptr 实现中，计算引用计数可能是昂贵的，甚至是不行的。这个函数不会抛出异常</span></span><br><span class="line">         <span class="function"><span class="keyword">long</span> <span class="title">use_count</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 这是个到 unspecified_bool_type 类型的隐式转换函数，它可以在Boolean上下文中测试一个智能指针。</span></span><br><span class="line">         <span class="comment">// 如果shared_ptr 保存着一个有效的指针，返回值为 True；否则为 false。</span></span><br><span class="line">         <span class="comment">// 注意，转换函数返回的类型是不确定的。</span></span><br><span class="line">         <span class="comment">// 把返回类型当成 bool 用会导致一些荒谬的操作，所以典型的实现采用了safe bool idiom,</span></span><br><span class="line">         <span class="comment">// 它很好地确保了只有可适用的Boolean测试可以使用。这个函数不会抛出异常</span></span><br><span class="line">         <span class="function"><span class="keyword">operator</span> <span class="title">unspecified_bool_type</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line">         </span><br><span class="line">         <span class="comment">// 这可以很方便地交换两个 shared_ptr。swap 函数交换保存的指针(以及它们的引用计数)。这个函数不会抛出异常。</span></span><br><span class="line">         <span class="function"><span class="keyword">void</span> <span class="title">swap</span><span class="params">(shared_ptr&lt;T&gt;&amp; b)</span></span>;</span><br><span class="line">     &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 要对保存在 shared_ptr 里的指针执行 static_cast，我们可以取出指针然后强制转换它，</span></span><br><span class="line">    <span class="comment">// 但我们不能把它存到另一个 shared_ptr 里；新的 shared_ptr 会认为它是第一个管理这些资源的。</span></span><br><span class="line">    <span class="comment">// 解决的方法是用static_pointer_cast. 使用这个函数可以确保被指物的引用计数保持正确。 static_pointer_cast 不会抛出异常</span></span><br><span class="line">     <span class="keyword">template</span> &lt;<span class="class"><span class="keyword">class</span> <span class="title">T</span>,<span class="keyword">class</span> <span class="title">U</span>&gt;</span> <span class="function">shared_ptr&lt;T&gt; <span class="title">static_pointer_cast</span><span class="params">(<span class="keyword">const</span> shared_ptr&lt;U&gt;&amp; r)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h4><p>​        使用 <code>shared_ptr</code> 解决的主要问题是知道删除一个被多个客户共享的资源的正确时机。如下所示的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;boost/shared_ptr.hpp&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    boost::shared_ptr&lt;<span class="keyword">int</span>&gt; no_;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">A</span>(boost::shared_ptr&lt;<span class="keyword">int</span>&gt; no):<span class="built_in">no_</span>(no)&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">value</span><span class="params">(<span class="keyword">int</span> i)</span></span>&#123;</span><br><span class="line">        *no_ = i;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    boost::shared_ptr&lt;<span class="keyword">int</span>&gt; no_;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">B</span>(boost::shared_ptr&lt;<span class="keyword">int</span>&gt; no):<span class="built_in">no_</span>(no)&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">value</span><span class="params">()</span> <span class="keyword">const</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> *no_;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">shared_ptr_test</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="comment">// 引用计数为 1</span></span><br><span class="line">    <span class="function">boost::shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">tmp</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">14</span>))</span></span>;</span><br><span class="line">    <span class="comment">// 引用计数为 2</span></span><br><span class="line">    <span class="function">A <span class="title">a</span><span class="params">(tmp)</span></span>;</span><br><span class="line">    std::cout &lt;&lt; tmp.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 引用计数为3</span></span><br><span class="line">    <span class="function">B <span class="title">b</span><span class="params">(tmp)</span></span>;</span><br><span class="line">    std::cout &lt;&lt; tmp.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    a.<span class="built_in">value</span>(<span class="number">28</span>);</span><br><span class="line">    std::cout &lt;&lt; b.<span class="built_in">value</span>() &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>程序正确调用后的输出结果为：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">3</span><br><span class="line">28</span><br></pre></td></tr></table></figure>

<p>​        上篇文章中使用了<code>scoped_ptr</code>实现了pImpl的用法，这种用法的累是不允许被复制的，那么 scoped_ptr 在保 存 pimpl 的动态分配实例时它工作得很好。但是这并不适合于所有想从 pimpl 用法中获益的类型(注意，你还可以 用 scoped_ptr ，但必须手工实现复制构造函数和赋值操作符)。对于那些可以处理共享的实现细节的类，应该用 shared_ptr 。当 pimpl 的所有权被传递给一个 shared_ptr, 复制和赋值操作都是免费的。</p>
<h4 id="share-ptr与标准库容器"><a href="#share-ptr与标准库容器" class="headerlink" title="share_ptr与标准库容器"></a>share_ptr与标准库容器</h4><p>​        把对象直接存入容器中有时会有些麻烦。以值的方式保存对象意味着使用者将获得容器中的元素的拷贝，对于那些复制是一种昂贵的操作的类型来说可能会有性能的问题。此外，有些容器，特别是 std::vector, 当你加入元素时可能会复制所有元素，这更加重了性能的问题。最后，传值的语义意味着没有多态的行为。如果你需要在容器中存放多态的对象而且你不想切割它 们，你必须用指针。如果你用裸指针，维护元素的完整性会非常复杂。从容器中删除元素时，你必须知道容器的使用者是否还在引用那些要删除的元素，不用担心多个使用者使用同一个元素。这些问题都可以用 <code>shared_ptr</code> 来解决。</p>
<p>​        把共享指针存入到标准库容器的例子：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;boost/shared_ptr.hpp&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">sing</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">A</span>() &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span> :</span> <span class="keyword">public</span> A</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">sing</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;hello ~~~ hello ~~~ hello ~~~~&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// factory function</span></span><br><span class="line"><span class="function">boost::shared_ptr&lt;A&gt; <span class="title">createA</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">boost::shared_ptr&lt;A&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> B())</span></span>;</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test_container</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">typedef</span> std::vector&lt;boost::shared_ptr&lt;A&gt;&gt; container_type;</span><br><span class="line">    <span class="keyword">typedef</span> container_type::iterator iterator;</span><br><span class="line"></span><br><span class="line">    container_type container;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)&#123;</span><br><span class="line">        container.<span class="built_in">push_back</span>(<span class="built_in">createA</span>());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(iterator it = container.<span class="built_in">begin</span>(); it != container.<span class="built_in">end</span>(); ++it)&#123;</span><br><span class="line">        (*it)-&gt;<span class="built_in">sing</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个程序有趣的是，即使A的析构函数没有本声明为<code>virtual</code>的，<code>shared_ptr</code>依然可以正确的调用B的析构函数。</p>
<h4 id="shared-ptr与其他资源"><a href="#shared-ptr与其他资源" class="headerlink" title="shared_ptr与其他资源"></a><code>shared_ptr</code>与其他资源</h4><p>​        有时你会发现你要把<code> shared_ptr</code> 用于某个特别的类型，它需要其它清除操作而不是简单的 <code>delete</code>。<code>shared_ptr</code> 可以通过客户化删除器来支持这种需要(自己定义资源回收释放)。那些处理象 <code>FILE*</code> 这样的操作系统句柄的资源通常要使用象<code> fclose</code> 这样的操作来释放。要在 <code>shared_ptr</code> 里使用 FILE* ，我们要定义一个类来负责释放相应的资源。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileCloser</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">FileCloser</span>() &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">(FILE *file)</span></span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;the file closer has been called with a FILE*, &quot;</span></span><br><span class="line">                     <span class="string">&quot;which will now be closed.\n&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span>(file != <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="built_in">fclose</span>(file);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test_filecloser</span><span class="params">()</span></span>&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;---------shared_ptr example with a custom dealloctor-------------.\n&quot;</span>;</span><br><span class="line">    &#123;</span><br><span class="line">        FILE* f = <span class="built_in">fopen</span>(<span class="string">&quot;test.txt&quot;</span>, <span class="string">&quot;w&quot;</span>);</span><br><span class="line">        <span class="comment">// 文件打开失败</span></span><br><span class="line">        <span class="keyword">if</span>(f == <span class="number">0</span>)&#123;</span><br><span class="line">            std::cout &lt;&lt; <span class="string">&quot;unable to open file.\n&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function">boost::shared_ptr&lt;FILE&gt; <span class="title">my_shared_file</span><span class="params">(f, FileCloser())</span></span>;</span><br><span class="line">        <span class="comment">// 定位文件指针</span></span><br><span class="line">        <span class="built_in">fseek</span>(my_shared_file.<span class="built_in">get</span>(), <span class="number">42</span>, SEEK_SET);</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;by now, the FILE has been closed\n&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        注意，在访问资源时，我们需要对 <code>shared_ptr </code>使用<code> &amp;*</code> 用法, <code>get</code>, 或 <code>get_pointer</code> 。(请注意最好使用<code> &amp;*</code>. 另两个选择不太清晰) 这个例子还可以更简单，如果我们在释放资源时只需要调用一个单参数函数的话，就根本不需要创建一个客户化删除器类型。上面的例子可以重写如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">     FILE* f=<span class="built_in">fopen</span>(<span class="string">&quot;test.txt&quot;</span>,<span class="string">&quot;r&quot;</span>);</span><br><span class="line">     <span class="keyword">if</span> (f==<span class="number">0</span>) &#123;</span><br><span class="line">     std::cout &lt;&lt; <span class="string">&quot;Unable to open file\n&quot;</span>;</span><br><span class="line">     <span class="keyword">throw</span> <span class="built_in">file_exception</span>();</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="function">boost::shared_ptr&lt;FILE&gt; <span class="title">my_shared_file</span><span class="params">(f,&amp;fclose)</span></span>;</span><br><span class="line">     <span class="comment">// 定位文件指针</span></span><br><span class="line">     <span class="built_in">fseek</span>(&amp;*my_shared_file,<span class="number">42</span>,SEEK_SET); </span><br><span class="line">&#125;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;By now, the FILE* has been closed!\n&quot;</span>;</span><br></pre></td></tr></table></figure>

<p>​        定制删除器在处理需要特殊释放程序的资源时非常有用。由于删除器不是 shared_ptr 类型的一部分，所以使用者 不需要知道关于智能指针所拥有的资源的任何信息(当然除了如何使用它！)。例如，你可以使用对象池，定制删除器 只需简单地把对象返还到池中。或者，<strong>一个 singleton 对象应该使用一个什么都不做的删除器。</strong></p>
<h4 id="从this创建shared-ptr"><a href="#从this创建shared-ptr" class="headerlink" title="从this创建shared_ptr"></a>从<code>this</code>创建<code>shared_ptr</code></h4><p>需要从 <code>this</code> 获得 <code>shared_ptr</code> ，即是说，你希望你的类被 <code>shared_ptr</code> 所管理，你需要把“自身”转换 为 <code>shared_ptr</code> 的方法。解决方案来自于我们即将讨论的另一个智能指针<code> boost:: weak_ptr</code>.<code> weak_ptr</code> 是 <code>shared_ptr</code> 的一个观察者；<strong>它只是安静地坐着并看着它们，但不会影响引用计数。</strong>通过存 储一个指向 <code>this</code> 的 <code>weak_ptr</code> 作为类的成员，就可以在需要的时候获得一个指向 <code>this</code> 的<code> shared_ptr</code> 。为了你 可以不必编写代码来保存一个指向 <code>this</code> 的 <code>weak_ptr</code> ，接着又从 <code>weak_ptr </code>获 <code>shared_ptr</code> 得，<code>Boost. Smart_ptr</code> 为这个任务提供了一个助手类，称为 <code>enable_shared_from_this</code>. 只要简单地让你的类公有地派生自 <code>enable_shared_from_this</code> ，然后在需要访问管理 <code>this</code> 的 <code>shared_ptr</code> 时，使用函数 <code>shared_from_this</code> 就 行了。下面的例子示范了如何使用 <code>enable_shared_from_this</code> ：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">do_stuff</span><span class="params">(boost::shared_ptr&lt;A&gt; p)</span></span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;do stuff...&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> :</span> <span class="keyword">public</span> boost::enable_shared_from_this&lt;A&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">call_do_stuff</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="built_in">do_stuff</span>(<span class="built_in">shared_from_this</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test_shared_from_this</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">boost::shared_ptr&lt;A&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> A())</span></span>;</span><br><span class="line">    p-&gt;<span class="built_in">call_do_stuff</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>enable_shared_from_this</code>的源码实现如下，可以看出，<code>enable_shared_from_this</code>使用了<code>weak_ptr</code>作为观察者：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">shared_ptr&lt;T&gt; <span class="title">shared_from_this</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;T&gt; <span class="title">p</span><span class="params">( weak_this_ )</span></span>;</span><br><span class="line">    <span class="built_in">BOOST_ASSERT</span>( p.<span class="built_in">get</span>() == <span class="keyword">this</span> );</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">shared_ptr&lt;T <span class="keyword">const</span>&gt; <span class="title">shared_from_this</span><span class="params">()</span> <span class="keyword">const</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">shared_ptr&lt;T <span class="keyword">const</span>&gt; <span class="title">p</span><span class="params">( weak_this_ )</span></span>;</span><br><span class="line">    <span class="built_in">BOOST_ASSERT</span>( p.<span class="built_in">get</span>() == <span class="keyword">this</span> );</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>在以下情况时使用 shared_ptr ：<br>● 当有多个使用者使用同一个对象，而没有一个明显的拥有者时<br>● 当要把指针存入标准库容器时<br>● 当要传送对象到库或从库获取对象，而没有明确的所有权时<br>● 当管理一些需要特殊清除方式的资源时（定制删除器）</p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">boost</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/03/30/boost%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%E2%80%94%E2%80%94shared-ptr/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-C-的智能指针和Boost的智能指针" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/03/25/C-%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%92%8CBoost%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/">C++的智能指针和Boost的智能指针</a>
    </h1>
  

        
        <a href="/2022/03/25/C-%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%92%8CBoost%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/" class="archive-article-date">
  	<time datetime="2022-03-25T12:43:46.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-03-25</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="C-的智能指针和Boost的智能指针"><a href="#C-的智能指针和Boost的智能指针" class="headerlink" title="C++的智能指针和Boost的智能指针"></a>C++的智能指针和Boost的智能指针</h3><h4 id="1-smart-ptr库如何改进你的程序？"><a href="#1-smart-ptr库如何改进你的程序？" class="headerlink" title="1. smart_ptr库如何改进你的程序？"></a>1. <code>smart_ptr</code>库如何改进你的程序？</h4><ul>
<li>使用<code>shared_ptr</code>进行对象的生存期自动管理，使得分享资源所有权变得有效且安全。</li>
<li>使用<code>weak_ptr</code>可以安全地观测共享资源，避免了悬挂的指针。</li>
<li>使用<code>scoped_ptr</code>和<code>scoped_array</code>限制资源的使用范围，使得代码更易于编写和维护，并有助于写出异常安全的代码。</li>
</ul>
<pre><code>    智能指针解决了资源生存期管理的问题(尤其是动态分配的对象). 智能指针有各种不同的风格。多数都有一种共同的关键特性：自动资源管理。这种特性可能以不同的方式出现：如动态分配对象的生存期控制，和获取及释放资源 (文件, 网络连接)。Boost 的智能指针主要针对第一种情况，它们保存指向动态分配对象的指针，并在正确的时候删除这些对象。
</code></pre>
<h4 id="2-何时需要智能指针？"><a href="#2-何时需要智能指针？" class="headerlink" title="2. 何时需要智能指针？"></a>2. 何时需要智能指针？</h4><p>三种典型的情况：</p>
<ul>
<li><p>资源所有权的共享<br>共享所有权是指两个或多个对象 需要同时使用第三个对象的情况。这第三个对象应该如何(或者说何时)被释放？为了确保释放的时机是正确的，每个使用这个共享资源的对象必须互相知道对方，才能准确掌握源的释放时间。从设计或维护的观点来看，这种耦合是不可行的。更好的方法是让这些资源所有者将资源的生存期管理责任委派给一个智能指针。当没有共享者存在时，智能指针就可以安全地释放这个资源了。</p>
</li>
<li><p>要写异常安全的代码时<br>异常安全，简单地说就是在异常抛出时没有资源泄漏并保证程序状态的一致性。如果一个对象是动态分配的，当异常抛出时它不会被删除。由于栈展开以及指针离开作用域，资源可以会泄漏直至程序结束(即使是程序结束时的资源回收也不是语言所保证的)。不仅可能程序会由于内存泄漏而耗尽资源，程序的状态也可能变得混乱。智能指针可以自动地为你释放这些资源，即使是在异常发生的情况下。</p>
</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 补充代码块，auto_ptr的异常不安全</span></span><br></pre></td></tr></table></figure>

<ul>
<li>避免常见的错误，如资源泄漏</li>
</ul>
<p>​    避免常见的错误。忘记调用 <code>delete</code> 是书本中最古老的错误(至少在这本书中)。一个智能指针不关心程序中的控制路径；它只关心在它所指向的对象的生存期结束时删除它。使用智能指针，你不再需要知道何时删除对象。并且，智能指针隐藏了释放资源的细节，因此使用者不需要知道是否要调用 delete, 有些特殊的清除函数并不总是删除资源的。</p>
<h4 id="3-Boost的智能指针——scoped-ptr和scoped-array"><a href="#3-Boost的智能指针——scoped-ptr和scoped-array" class="headerlink" title="3. Boost的智能指针——scoped_ptr和scoped_array"></a>3. Boost的智能指针——<code>scoped_ptr</code>和<code>scoped_array</code></h4><h5 id="3-1-scope-ptr"><a href="#3-1-scope-ptr" class="headerlink" title="3.1 scope_ptr"></a>3.1 <code>scope_ptr</code></h5><h6 id="3-1-1-头文件："><a href="#3-1-1-头文件：" class="headerlink" title="3.1.1 头文件："></a>3.1.1 头文件：</h6><p><code>&quot;boost/scoped_ptr.hpp&quot;</code></p>
<h6 id="3-1-2-简介："><a href="#3-1-2-简介：" class="headerlink" title="3.1.2 简介："></a>3.1.2 简介：</h6><p>​    <code>boost::scoped_ptr</code> 用于确保能够正确地删除动态分配的对象。<code>scoped_ptr</code> 有着与 <code>std::auto_ptr</code> 类似的特性，而<strong>最大的区别在于它不能转让所有权</strong>而 <code>auto_ptr</code> 可以。事实上，<code>scoped_ptr</code> 永远不能被复制或被赋值！<code>scoped_ptr</code>是一种轻量级的指针；使用它不会使你的程序变大或者变慢。只会让你的代码更安全更易于维护。</p>
<h6 id="3-1-3-源码摘要："><a href="#3-1-3-源码摘要：" class="headerlink" title="3.1.3 源码摘要："></a>3.1.3 源码摘要：</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> boost &#123;</span><br><span class="line"> <span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt; <span class="class"><span class="keyword">class</span> <span class="title">scoped_ptr</span> :</span> noncopyable &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">scoped_ptr</span><span class="params">(T* p = <span class="number">0</span>)</span></span>; <span class="comment">//构造函数</span></span><br><span class="line"> ~<span class="built_in">scoped_ptr</span>(); <span class="comment">// 析构函数</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">reset</span><span class="params">(T* p = <span class="number">0</span>)</span></span>; </span><br><span class="line"> <span class="comment">// 操作符重载</span></span><br><span class="line"> T&amp; <span class="keyword">operator</span>*() <span class="keyword">const</span>; </span><br><span class="line"> T* <span class="keyword">operator</span>-&gt;() <span class="keyword">const</span>; </span><br><span class="line"> <span class="function">T* <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span></span>; </span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">swap</span><span class="params">(scoped_ptr&amp; b)</span></span>; </span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt; </span></span><br><span class="line"><span class="function"> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(scoped_ptr&lt;T&gt; &amp; a, scoped_ptr&lt;T&gt; &amp; b)</span></span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<table>
<thead>
<tr>
<th>函数</th>
<th>属性</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td><code>explicit scoped_ptr(T* p=0)</code></td>
<td>成员函数</td>
<td align="left">构造函数，存储 p的一份拷贝。注意，p 必须是用<code>operator new</code> 分配的，或者是<code> null</code>. 在构造的时候，不要求T必须是一个完整的类型。当指针 p 是用某个分配函数的结果而不是直接调用 new 得到的时候很有用：因为这个类型不必是完整的，只需要类型 T 的一个前向声明就可以了。这个构造函数不会抛出异常</td>
</tr>
<tr>
<td><code>~scoped_ptr()</code></td>
<td>成员函数</td>
<td align="left">删除被指物。类型 T 在被销毁时必须是一个完整的类型。如果 <code>scoped_ptr</code> 在它被析构时并没有保存资源，它就 什么都不做。这个析构函数不会抛出异常。</td>
</tr>
<tr>
<td><code>void reset(T* p=0);</code></td>
<td>成员函数</td>
<td align="left">重置一个 <code>scoped_ptr </code>就是删除它已保存的指针，如果它有的话，并重新保存 p. 通常，资源的生存期管理应该完 全由<code>scoped_ptr</code> 自己处理，但是在极少数时候，资源需要在 <code>scoped_ptr</code> 的析构之前释放，或者 <code>scoped_ptr </code>要处理它原有资源外的另外一个资源。这时，就可以用 <code>reset</code>，<strong>但一定要尽量少用它。(过多地使 用它通常表示有设计方面的问题) 这个函数不会抛出异常。</strong></td>
</tr>
<tr>
<td><code>T&amp; operator*() const;</code></td>
<td>成员函数</td>
<td align="left">返回保存的指针。如果保存的指针为空，则调用这个函数会导致未定义行为。如果不能肯定指针是否空的，最好使 用函数get。<strong>这个函数不会抛出异常</strong></td>
</tr>
<tr>
<td><code>T* operator-&gt;() const;</code></td>
<td>成员函数</td>
<td align="left">返回保存的指针。如果保存的指针为空，则调用这个函数会导致未定义行为。如果不能肯定指针是否空的，最好使 用函数get。<strong>这个函数不会抛出异常</strong></td>
</tr>
<tr>
<td><code>T* get() const;</code></td>
<td>成员函数</td>
<td align="left">返回保存的指针。应该小心地使用get，因为它可以直接操作裸指针。但是，get使得你可以测试保存的指针是否 为空。这个函数不会抛出异常。get通常用于调用那些需要裸指针的函数</td>
</tr>
<tr>
<td><code>operator unspecified_bool_type() const</code></td>
<td>成员函数</td>
<td align="left">返回 <code>scoped_ptr </code>是否为非空。返回值的类型是未指明的，但这个类型可被用于 Boolean 的上下文中。在 if 语 句中最好使用这个类型转换函数，而不要用 get 去测试 scoped_ptr 的有效性</td>
</tr>
<tr>
<td><code>void swap(scoped_ptr&amp; b)</code></td>
<td>成员函数</td>
<td align="left">交换两个<code>scoped_ptr</code>的内容。这个函数不会抛出异常</td>
</tr>
<tr>
<td><code>template void swap(scoped_ptr&amp; a,scoped_ptr&amp; b)</code></td>
<td>普通函数</td>
<td align="left">这个函数提供了交换两个scoped pointer的内容的更好的方法。之所以说它更好，是因为 <code>swap(scoped1, scoped2)</code> 可以更广泛地用于很多指针类型，包括裸指针和第三方的智能指针。<code>scoped1.swap(scoped2)</code>则 只能用于它的定义所在的智能指针，而不能用于裸指针。</td>
</tr>
</tbody></table>
<h6 id="3-1-4-用法："><a href="#3-1-4-用法：" class="headerlink" title="3.1.4 用法："></a>3.1.4 用法：</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="function">boost::scoped_ptr&lt;std::string&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> std::string(<span class="string">&quot;this is a scope_ptr&quot;</span>))</span></span>;</span><br><span class="line">    <span class="keyword">if</span>(p)</span><br><span class="line">        std::cout &lt;&lt; *p &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 利用智能指针调用字符串的成员函数</span></span><br><span class="line">    <span class="keyword">size_t</span> length = p-&gt;<span class="built_in">size</span>();</span><br><span class="line">    std::cout &lt;&lt; length &lt;&lt;std::endl;</span><br><span class="line">    <span class="comment">// 解引用的操作符</span></span><br><span class="line">    *p = <span class="string">&quot;hello scope_ptr&quot;</span>;</span><br><span class="line">    std::cout &lt;&lt; *p &lt;&lt; std::endl;</span><br><span class="line">&#125;<span class="comment">// 离开作用域，scoped_ptr被销毁</span></span><br></pre></td></tr></table></figure>

<p>​    <strong>不要手动删除</strong>，scoped_ptr 可以测试其有效性，就象一个普通指针那样，因为它提 供了隐式转换到一个可用于布尔表达式的类型的方法。其次，可以象使用裸指针那样调用被被指物的成员函数，因 为重载了<code> operator-&gt;</code>. 第三，也可以和裸指针一样解引用 <code>scoped_ptr</code>，这归功<code>operator*</code> 的重载。这些特 性正是 <code>scoped_ptr </code>和其它智能指针的用处所在，因为它们和裸指针的不同之处在于对生存期管理的语义上，而 不在于语法上。</p>
<h6 id="3-1-5-auto-ptr和scope-ptr"><a href="#3-1-5-auto-ptr和scope-ptr" class="headerlink" title="3.1.5 auto_ptr和scope_ptr"></a>3.1.5 <code>auto_ptr</code>和<code>scope_ptr</code></h6><p>  <code>scoped_ptr</code> 与 <code>auto_ptr</code> 间的区别主要在于对拥有权的处理。<code>auto_ptr</code> 在复制时会从源 <code>auto_ptr</code> 自动交出拥有权，而 <code>scoped_ptr</code> 则不允许被复制。</p>
  <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">scoped_vs_auto</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">boost::scoped_ptr&lt;std::string&gt; <span class="title">p_scope</span><span class="params">(<span class="keyword">new</span> std::string(<span class="string">&quot;Hello world&quot;</span>))</span></span>;</span><br><span class="line">    <span class="function">std::auto_ptr&lt;std::string&gt; <span class="title">p_auto</span><span class="params">(<span class="keyword">new</span> std::string(<span class="string">&quot;hello&quot;</span>))</span></span>;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;p_scope-&gt;size() = &quot;</span> &lt;&lt; p_scope-&gt;<span class="built_in">size</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;p_auto-&gt;size() = &quot;</span>&lt;&lt; p_auto-&gt;<span class="built_in">size</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 区别之处</span></span><br><span class="line">    boost::scoped_ptr&lt;std::string&gt; p_another_scope = p_scope; <span class="comment">// 这句会报错因为 scoped_pt r不能被复制构造或被赋值。</span></span><br><span class="line">    std::auto_ptr&lt;std::string&gt; p_another_auto = p_auto;<span class="comment">// 在完成赋值后，p_auto就只剩下一个空指针</span></span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;p_another_auto-&gt;size() = &quot;</span> &lt;&lt; p_another_auto-&gt;<span class="built_in">size</span>() &lt;&lt; std::endl;</span><br><span class="line">    (*p_auto).<span class="built_in">size</span>();<span class="comment">// 这里很危险，测试情况下程序异常结束!!!!!!!!!!!!!!!!!!</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个例子不能通过编译，因为 <code>scoped_ptr</code>不能被复制构造或被赋值。源代码如下，可以看出 <code>scoped_ptr</code>的拷贝构造函数被声明在private中：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="class"><span class="keyword">class</span> <span class="title">T</span>&gt;</span> <span class="class"><span class="keyword">class</span> <span class="title">scoped_ptr</span> // <span class="title">noncopyable</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T * px;</span><br><span class="line">    <span class="built_in">scoped_ptr</span>(scoped_ptr <span class="keyword">const</span> &amp;);</span><br><span class="line">    scoped_ptr &amp; <span class="keyword">operator</span>=(scoped_ptr <span class="keyword">const</span> &amp;);</span><br><span class="line">    <span class="keyword">typedef</span> scoped_ptr&lt;T&gt; this_type;</span><br><span class="line">    <span class="keyword">void</span> <span class="keyword">operator</span>==( scoped_ptr <span class="keyword">const</span>&amp; ) <span class="keyword">const</span>;</span><br><span class="line">    <span class="keyword">void</span> <span class="keyword">operator</span>!=( scoped_ptr <span class="keyword">const</span>&amp; ) <span class="keyword">const</span>;</span><br><span class="line">    .....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><code>auto_ptr </code>既可以复制构造也可以赋值， 但这们同时也意味着它把所有权从<code>p_auto</code>转移给了 <code>p_another_auto</code>, 在赋值后<code> p_auto</code> 将只剩下一个空指针。</p>
<p>建议：</p>
<blockquote>
<ol>
<li>永远不要把 <code>auto_ptr</code> 放入标准库的容器里。如果你试一下，通常你会得到一个编译错误；如 果你没有得到错误，你就麻烦了。</li>
<li>由于 <code>scoped_ptr::get </code>会返回一个裸指针，所以就有可能对 <code>scoped_ptr</code> 做一些有害的事情，其中有两件是你 尤其要避免的。第一，不要删除这个裸指针。因为它会在 <code>scoped_ptr</code> 被销毁时再一次被删除。第二，不要把这 个裸指针保存到另一个<code> scoped_ptr</code> (或其它任何的智能指针)里。因为这样也会两次删除这个指针，每个<code> scoped_ptr</code> 一次。简单地说，尽量少用 get, 除非你要使用那些要求你传送裸指针的遗留代码！</li>
</ol>
</blockquote>
<h6 id="3-1-6-scope-ptr和pImpl用法"><a href="#3-1-6-scope-ptr和pImpl用法" class="headerlink" title="3.1.6 scope_ptr和pImpl用法"></a>3.1.6 scope_ptr和pImpl用法</h6><p>​      由于客户是依赖于类的头文件的，头文件中的任何 变化都会影响客户，即使仅是对私有节或保护节 的修改。pimpl 用法隐藏了这些细节，方法是将私有数据和函数 放入一个单独的类中，并保存在一个实现文件中，然后在头文件中对这个类进行前向声明并保存 一个指向该实现 类的指针。类的构造函数分配这个 pimpl 类，而析构函数则释放它。这样可以消除头文件与实现细节的相关性。 我们来构造一个实现 pimpl 用法的类，然后用智能指针让它更安全些。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//.h</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> PIMPL_SAMPLE_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PIMPL_SAMPLE_H</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 前置声明</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Impl</span>;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">pimpl_sample</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">pimpl_sample</span>();</span><br><span class="line">    ~<span class="built_in">pimpl_sample</span>();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dosomething</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    Impl *m_pImpl;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">// PIMPL_SAMPLE_H</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// .cpp</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&quot;pimpl_sample.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Impl</span>&#123;</span></span><br><span class="line">    std::string s;</span><br><span class="line">    <span class="comment">// 一个成员函数</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dosomething</span><span class="params">()</span></span>&#123;</span><br><span class="line">        std::cout &lt;&lt; s &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">pimpl_sample::<span class="built_in">pimpl_sample</span>():<span class="built_in">m_pImpl</span>(<span class="keyword">new</span> Impl)</span><br><span class="line">&#123;</span><br><span class="line">    m_pImpl-&gt;s = <span class="string">&quot;this is Impl idiom&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">pimpl_sample::~<span class="built_in">pimpl_sample</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">delete</span> m_pImpl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pimpl_sample::dosomething</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    m_pImpl-&gt;<span class="built_in">dosomething</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>分析：上述代码看起来很完美，但是其实并非如此，这个实现不是异常安全的！是<code> pimpl_sample</code> 的构造函数有可能在 <code>Impl </code>被 构造后抛出一个异常。在构造函数中抛出异常意味着已构造的对象并不存在，因此在栈展开时将不会调用它的析构 函数。这样就意味着分配给<code> m_pImpl</code>指针的内存将泄漏。然而，有一样简单的解决方法：用<code>scoped_ptr </code>来解救！</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">pimpl_sample</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">pimpl_sample</span>();</span><br><span class="line">    ~<span class="built_in">pimpl_sample</span>();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dosomething</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    boost::scoped_ptr&lt;Impl&gt; m_pImpl;<span class="comment">// 利用智能指针解决问题</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    让 <code>scoped_ptr </code>来处理隐藏类 <code>Impl</code> 的生存期管理，并从析构函数中去掉对 <code>Impl</code> 的删除，这样就做完了。但是，你必须记住要手工定义析构函数；原因是在编译器生成隐式析构函数 时，类 <code>Impl</code> 还是不完整的，所以它的析构函数不能被调用。如果你用 <code>auto_ptr</code> 来保存 <code>Impl</code>, 你可以编译，但也还是有这个问题，但如果用<code>scoped_ptr</code>, 你将收到一个错误提示。</p>
<p>在以下情况使用<code>scoped_ptr</code>：</p>
<ul>
<li>在可能有异常抛出的作用域使用指针</li>
<li>函数里有几条控制路径</li>
<li>动态分配对象的生存期应被限制于特定的作用域内</li>
<li>异常安全非常重要时</li>
</ul>
<h5 id="3-2-scoped-array"><a href="#3-2-scoped-array" class="headerlink" title="3.2 scoped_array"></a>3.2 scoped_array</h5><p>​    需要动态分配数组时，通常最好用 std::vector 来实现，但是有两种情形，看起来用数组更应该：一种是为了优化，用 vector 多少有一些额外的内存和速度开销；另一种是为了某种原因，必须要求数组的大小是固定的。没有非常清晰的优点。实际上，<strong>最好还是用 std::vector</strong> ，除非性能测试表示 scoped_array 的好处是可保证的。</p>
<p>​    但是记住，多数情况下应该使用 std::vector，它更灵活、更强大。只有当你需要 确保数组的大小是固定的时候，才使用 scoped_array 来替代 std::vector</p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">智能指针</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/03/25/C-%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%92%8CBoost%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Linux-GDB调试和ldd指令" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/03/13/Linux-GDB%E8%B0%83%E8%AF%95%E5%92%8Cldd%E6%8C%87%E4%BB%A4/">Linux GDB调试和ldd指令</a>
    </h1>
  

        
        <a href="/2022/03/13/Linux-GDB%E8%B0%83%E8%AF%95%E5%92%8Cldd%E6%8C%87%E4%BB%A4/" class="archive-article-date">
  	<time datetime="2022-03-12T16:02:25.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-03-13</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h4 id="ldd-查看程序依赖库"><a href="#ldd-查看程序依赖库" class="headerlink" title="ldd 查看程序依赖库"></a>ldd 查看程序依赖库</h4><ul>
<li><p>ldd</p>
<p>作用：用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题。</p>
</li>
</ul>
<p>示例：查看test程序运行所依赖的库:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">/opt/app/todeav1/test$ldd test</span><br><span class="line">libstdc++.so.6 =&gt; /usr/lib64/libstdc++.so.6 (0x00000039a7e00000)</span><br><span class="line">libm.so.6 =&gt; /lib64/libm.so.6 (0x0000003996400000)</span><br><span class="line">libgcc_s.so.1 =&gt; /lib64/libgcc_s.so.1 (0x00000039a5600000)</span><br><span class="line">libc.so.6 =&gt; /lib64/libc.so.6 (0x0000003995800000)</span><br><span class="line">/lib64/ld-linux-x86-64.so.2 (0x0000003995400000)</span><br></pre></td></tr></table></figure>

<ul>
<li>第一列：程序需要依赖什么库</li>
<li>第二列: 系统提供的与程序需要的库所对应的库</li>
<li>第三列：库加载的开始地址</li>
</ul>
<p>通过上面的信息，我们可以得到以下几个信息：</p>
<ol>
<li>通过对比第一列和第二列，我们可以分析程序需要依赖的库和系统实际提供的，是否相匹配</li>
<li>通过观察第三列，我们可以知道在当前的库中的符号在对应的进程的地址空间中的开始位置</li>
</ol>
<p>如果依赖的某个库找不到，通过这个命令可以迅速定位问题所在；</p>
<p>注解：</p>
<p>原理： ldd不是个可执行程式，而只是个shell脚本； ldd显示可执行模块的dependency的工作原理，其实质是通过ld-linux.so（elf动态库的装载器）来实现的。ld-linux.so模块会先于executable模块程式工作，并获得控制权，因此当上述的那些环境变量被设置时，ld-linux.so选择了显示可执行模块的dependency。</p>
<h4 id="1-gdb-调试利器"><a href="#1-gdb-调试利器" class="headerlink" title="1. gdb 调试利器"></a>1. gdb 调试利器</h4><p>GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c++程序员，gdb是必不可少的工具；</p>
<h5 id="1-1-启动gdb"><a href="#1-1-启动gdb" class="headerlink" title="1.1. 启动gdb"></a>1.1. 启动gdb</h5><p>对C/C++程序的调试，需要在编译前就加上-g选项:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$g++ -g hello.cpp -o hello</span><br></pre></td></tr></table></figure>

<p>调试可执行文件:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$gdb &lt;program&gt;</span><br></pre></td></tr></table></figure>

<p>program也就是你的执行文件，一般在当前目录下。</p>
<p>调试core文件(core是程序非法执行后core dump后产生的文件):</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$gdb &lt;program&gt; &lt;core dump file&gt;</span><br><span class="line">$gdb program core.11127</span><br></pre></td></tr></table></figure>

<p>调试服务程序:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$gdb &lt;program&gt; &lt;PID&gt;</span><br><span class="line">$gdb hello 11127</span><br></pre></td></tr></table></figure>

<p>如果你的程序是一个服务程序，那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去，并调试他。program应该在PATH环境变量中搜索得到。</p>
<h5 id="1-2-gdb交互命令"><a href="#1-2-gdb交互命令" class="headerlink" title="1.2. gdb交互命令"></a>1.2. gdb交互命令</h5><p>启动gdb后，进入到交互模式，通过以下命令完成对程序的调试；注意高频使用的命令一般都会有缩写，熟练使用这些缩写命令能提高调试的效率；</p>
<h5 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h5><ul>
<li>run：简记为 r ，其作用是运行程序，当遇到断点后，程序会在断点处停止运行，等待用户输入下一步的命令。</li>
<li>continue （简写c ）：继续执行，到下一个断点处（或运行结束）</li>
<li>next：（简写 n），单步跟踪程序，当遇到函数调用时，也不进入此函数体；此命令同 step 的主要区别是，step 遇到用户自定义的函数，将步进到函数中去运行，而 next 则直接调用函数，不会进入到函数体内。</li>
<li>step （简写s）：单步调试如果有函数调用，则进入函数；与命令n不同，n是不进入调用的函数的</li>
<li>until：当你厌倦了在一个循环体内单步跟踪时，这个命令可以运行程序直到退出循环体。</li>
<li>until+行号： 运行至某行，不仅仅用来跳出循环</li>
<li>finish： 运行程序，直到当前函数完成返回，并打印函数返回时的堆栈地址和返回值及参数值等信息。</li>
<li>call 函数(参数)：调用程序中可见的函数，并传递“参数”，如：call gdb_test(55)</li>
<li>quit：简记为 q ，退出gdb</li>
</ul>
<h5 id="设置断点"><a href="#设置断点" class="headerlink" title="设置断点"></a>设置断点</h5><ul>
<li><p>break n （简写b n）:在第n行处设置断点</p>
<p>（可以带上代码路径和代码名称： b OAGUPDATE.cpp:578）</p>
</li>
<li><p>b fn1 if a＞b：条件断点设置</p>
</li>
<li><p>break func（break缩写为b）：在函数func()的入口处设置断点，如：break cb_button</p>
</li>
<li><p>delete 断点号n：删除第n个断点</p>
</li>
<li><p>disable 断点号n：暂停第n个断点</p>
</li>
<li><p>enable 断点号n：开启第n个断点</p>
</li>
<li><p>clear 行号n：清除第n行的断点</p>
</li>
<li><p>info b （info breakpoints） ：显示当前程序的断点设置情况</p>
</li>
<li><p>delete breakpoints：清除所有断点：</p>
</li>
</ul>
<h5 id="查看源代码"><a href="#查看源代码" class="headerlink" title="查看源代码"></a>查看源代码</h5><ul>
<li>list ：简记为 l ，其作用就是列出程序的源代码，默认每次显示10行。</li>
<li>list 行号：将显示当前文件以“行号”为中心的前后10行代码，如：list 12</li>
<li>list 函数名：将显示“函数名”所在函数的源代码，如：list main</li>
<li>list ：不带参数，将接着上一次 list 命令的，输出下边的内容。</li>
</ul>
<h5 id="打印表达式"><a href="#打印表达式" class="headerlink" title="打印表达式"></a>打印表达式</h5><ul>
<li>print 表达式：简记为 p ，其中“表达式”可以是任何当前正在被测试程序的有效表达式，比如当前正在调试C语言的程序，那么“表达式”可以是任何C语言的有效表达式，包括数字，变量甚至是函数调用。</li>
<li>print a：将显示整数 a 的值</li>
<li>print ++a：将把 a 中的值加1,并显示出来</li>
<li>print name：将显示字符串 name 的值</li>
<li>print gdb_test(22)：将以整数22作为参数调用 gdb_test() 函数</li>
<li>print gdb_test(a)：将以变量 a 作为参数调用 gdb_test() 函数</li>
<li>display 表达式：在单步运行时将非常有用，使用display命令设置一个表达式后，它将在每次单步进行指令后，紧接着输出被设置的表达式及值。如： display a</li>
<li>watch 表达式：设置一个监视点，一旦被监视的“表达式”的值改变，gdb将强行终止正在被调试的程序。如： watch a</li>
<li>whatis ：查询变量或函数</li>
<li>info function： 查询函数</li>
<li>扩展info locals： 显示当前堆栈页的所有变量</li>
</ul>
<h5 id="查询运行信息"><a href="#查询运行信息" class="headerlink" title="查询运行信息"></a>查询运行信息</h5><ul>
<li>where/bt ：当前运行的堆栈列表；</li>
<li>bt backtrace 显示当前调用堆栈</li>
<li>up/down 改变堆栈显示的深度</li>
<li>set args 参数:指定运行时的参数</li>
<li>show args：查看设置好的参数</li>
<li>info program： 来查看程序的是否在运行，进程号，被暂停的原因。</li>
</ul>
<h5 id="分割窗口"><a href="#分割窗口" class="headerlink" title="分割窗口"></a>分割窗口</h5><ul>
<li>layout：用于分割窗口，可以一边查看代码，一边测试：</li>
<li>layout src：显示源代码窗口</li>
<li>layout asm：显示反汇编窗口</li>
<li>layout regs：显示源代码/反汇编和CPU寄存器窗口</li>
<li>layout split：显示源代码和反汇编窗口</li>
<li>Ctrl + L：刷新窗口</li>
</ul>
<p>注解</p>
<p>交互模式下直接回车的作用是重复上一指令，对于单步调试非常方便；</p>
<h4 id="1-3-更强大的工具"><a href="#1-3-更强大的工具" class="headerlink" title="1.3. 更强大的工具"></a>1.3. 更强大的工具</h4><h5 id="cgdb"><a href="#cgdb" class="headerlink" title="cgdb"></a>cgdb</h5><p>cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示，这无疑增加了调试的方便性，提高了调试效率。界面类似vi，符合unix/linux下开发人员习惯;如果熟悉gdb和vi，几乎可以立即使用cgdb。</p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">linux</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/03/13/Linux-GDB%E8%B0%83%E8%AF%95%E5%92%8Cldd%E6%8C%87%E4%BB%A4/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-设计模式——Factory-Method" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/16/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94Factory-Method/">设计模式——Factory Method</a>
    </h1>
  

        
        <a href="/2022/02/16/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94Factory-Method/" class="archive-article-date">
  	<time datetime="2022-02-16T13:34:48.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-16</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h4 id="Factory-Method"><a href="#Factory-Method" class="headerlink" title="Factory Method"></a>Factory Method</h4><h5 id="1-问题引出"><a href="#1-问题引出" class="headerlink" title="1 问题引出"></a>1 问题引出</h5><p>设想有个这样的场景，制作一个文件分割器，文件分割器支持不同类型的文件分割，例如二进制文件、文本文件、图像文件和视频文件等，为了应对这些变化，就需要一个抽象基类并让不同的类继承这个抽象基类，并重写实现<code>virtual void spilt() = 0</code>方法，所以可以写出如下所示的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 接口类</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ISpilter</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">spilt</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">ISpilter</span>() &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BinarySpilter</span> :</span> <span class="keyword">public</span> ISpilter</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">spilt</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">// 二进制文件分割</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 文本文件分割</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TxtSpilter</span> :</span> <span class="keyword">public</span> ISpilter</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">spilt</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">// 二进制文件分割</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 图片分割</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PictureSpilter</span> :</span> <span class="keyword">public</span> ISpilter</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">spilt</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">// 图像文件文件分割</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 视频文件分割器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VideoSpilter</span> :</span> <span class="keyword">public</span> ISpilter</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">spilt</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">// 视频文件分割</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在文件分割器的主窗口中，对文件分割器对象进行初始化，本例中当点击按钮时，执行文件分割，主窗口的代码可以简化为如下所示的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&quot;fileSplitter&quot;</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MainFrm</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MainFrm</span>(<span class="comment">/* args */</span>);</span><br><span class="line">    ~<span class="built_in">MainFrm</span>();</span><br><span class="line">    <span class="comment">// 点击按钮，执行文件分割</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">on_btn_click</span><span class="params">()</span></span>&#123;</span><br><span class="line">        ISpilter * m_pSpiltter </span><br><span class="line">        = <span class="keyword">new</span> BinarySpilter; <span class="comment">// 这里依赖了具体的类</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>问题出在了这句代码中：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ISpilter * m_pSpiltter = <span class="keyword">new</span> BinarySpilter; <span class="comment">// 这里依赖了具体的类</span></span><br></pre></td></tr></table></figure>

<p>这违反了<strong>依赖倒置原则</strong>，<code>ISpilter * m_pSpiltter </code>是一个抽象依赖，而<code>= new BinarySpilter;</code>是细节依赖。这就产生了编译时的细节依赖，<code>MainFrm</code>在编译时，<code>BinarySpilter</code>必须存在。如何解决这个问题呢？若是仅仅像下列这样，显然C++的语法并不支持：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ISpilter * m_pSpiltter = <span class="keyword">new</span> ISpilter; <span class="comment">// 这里依赖了具体的类</span></span><br></pre></td></tr></table></figure>

<p>所以工厂模式的动机就是在软件系统中，经常面临着创建对象的工作，由于需求的变化，需要创建的对象的具体类型经常变化。new依赖具体细节，通过对象创建模式绕开new，从而避免对象创建（new）过程中所导致的紧耦合（依赖具体类），从而支持对象创建的稳定，它是面向接口编程的必然需求。</p>
<h5 id="2-绕开new的方式去创建对象——工厂模式"><a href="#2-绕开new的方式去创建对象——工厂模式" class="headerlink" title="2 绕开new的方式去创建对象——工厂模式"></a>2 绕开new的方式去创建对象——工厂模式</h5><p>利用一函数返回一个对象。例如写出一个这样的类，如下所示：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SplitterFactry</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">ISpilter <span class="title">CreateSplitter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> BinarySpilter;<span class="comment">// 这里仍然存在问题，依赖细节</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在主框架中的调用代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MainFrm.cpp</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MainFrm</span>(<span class="comment">/* args */</span>);</span><br><span class="line">    ~<span class="built_in">MainFrm</span>();</span><br><span class="line">    <span class="comment">// 点击按钮，执行文件分割</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">on_btn_click</span><span class="params">()</span></span>&#123;</span><br><span class="line">        SplitterFactry factory;</span><br><span class="line">        ISpilter * m_pSpiltter </span><br><span class="line">        = factory.<span class="built_in">CreateSplitter</span>(); <span class="comment">// 这里依赖了具体的类</span></span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>这段代码仍然存在问题，仍然存在依赖关系。利用C++语言的多态性对这段代码继续改进，则<code>SplitterFactry</code>可以优化为下列的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SplitterFactry</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> ISpilter <span class="title">CreateSplitter</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">SplitterFactry</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>框架中的调用方式也应该得到相应的变化：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MainFrm</span>(<span class="comment">/* args */</span>);</span><br><span class="line">    ~<span class="built_in">MainFrm</span>();</span><br><span class="line">    <span class="comment">// 点击按钮，执行文件分割</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">on_btn_click</span><span class="params">()</span></span>&#123;</span><br><span class="line">        SplitterFactry *factory;</span><br><span class="line">        ISpilter * m_pSpiltter </span><br><span class="line">        = factory-&gt;<span class="built_in">CreateSplitter</span>(); <span class="comment">// 利用多态进行创建</span></span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>所以接下来对所有的具体类创建工厂，并重写基类中的虚函数，代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建具体工厂</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BinarySpilterFactory</span> :</span> <span class="keyword">public</span> SplitterFactry</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> ISpilter *<span class="title">CreateSplitter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">BinarySpilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TxtSpilterFactory</span> :</span> <span class="keyword">public</span> SplitterFactry</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> ISpilter *<span class="title">CreateSplitter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">TxtSpilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PictureSpilterFactory</span> :</span> <span class="keyword">public</span> SplitterFactry</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> ISpilter *<span class="title">CreateSplitter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">PictureSpilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VideoSpilterFactory</span> :</span> <span class="keyword">public</span> SplitterFactry</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">/* data */</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> ISpilter *<span class="title">CreateSplitter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">VideoSpilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在主框架中，工厂对象声明的位置需要变为成员变量，将来是从外界传入具体的工厂对象的，如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    SplitterFactry *factory;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MainFrm</span>(SplitterFactry *theFactory)&#123;</span><br><span class="line">        factory = theFactory;</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">MainFrm</span>();</span><br><span class="line">    <span class="comment">// 点击按钮，执行文件分割</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">on_btn_click</span><span class="params">()</span></span>&#123;</span><br><span class="line">        ISpilter * m_pSpiltter </span><br><span class="line">        = factory-&gt;<span class="built_in">CreateSplitter</span>(); <span class="comment">// 利用多态进行创建</span></span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>改良后的代码，<code>MainFrm</code>再也没有依赖具体的类了。</p>
<h5 id="GOF对工厂模式的定义和结构图"><a href="#GOF对工厂模式的定义和结构图" class="headerlink" title="GOF对工厂模式的定义和结构图"></a>GOF对工厂模式的定义和结构图</h5><p>定义一个用于创建对象的接口，让子类决定实例化哪一个类。Factory Method使得一个类的实例化过程延迟（目的：解耦，手段：虚函数）到子类。Factory Method的结构图如下所示：</p>
<p><img src="https://s2.loli.net/2022/02/16/F9nmCldQZiAeS14.jpg" alt="Factory.jpg"></p>
<center>图 Factory Method结构图</center>

<h5 id="要点总结"><a href="#要点总结" class="headerlink" title="要点总结"></a>要点总结</h5><ol>
<li>Factory Method模式用于隔离对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型，紧耦合关系（new）会导致软件的脆弱；</li>
<li>Factory Method模式通过面向对象的手法（利用多态的性质），将所要创建的具体对象工作延迟到子类，从而实现一种扩展（而非更改）的策略，较好的解决了这种紧耦合关系。</li>
<li>Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。</li>
</ol>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/16/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94Factory-Method/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-代码整洁之道——如何写好函数？" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/15/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%E2%80%94%E2%80%94%E5%A6%82%E4%BD%95%E5%86%99%E5%A5%BD%E5%87%BD%E6%95%B0%EF%BC%9F/">代码整洁之道——如何写好函数？</a>
    </h1>
  

        
        <a href="/2022/02/15/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%E2%80%94%E2%80%94%E5%A6%82%E4%BD%95%E5%86%99%E5%A5%BD%E5%87%BD%E6%95%B0%EF%BC%9F/" class="archive-article-date">
  	<time datetime="2022-02-15T14:33:22.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-15</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="代码整洁——如何写好函数"><a href="#代码整洁——如何写好函数" class="headerlink" title="代码整洁——如何写好函数"></a>代码整洁——如何写好函数</h3><h4 id="短小"><a href="#短小" class="headerlink" title="短小"></a>短小</h4><p>函数的第一规则是短小，第二条规则还是TMD短小！！！函数不应该有100行那么长，20行封顶最佳。一个好的函数应该有这样的特征：每个函数都只有两行、三行或者四行那么长，每个函数都一目了然，每个函数都依次序把你带到下一个函数。<br>对于代码块和缩进，<strong>if语句、else语句、while语句等，其中的代码块应该只有一行</strong>。这样不但能保持函数短小，而且，因为块内调用的函数具有说明性的名称，从而增加了文档的价值。这意味着函数不应该大到足以容纳嵌套结构，所以函数的缩进层级不该多于一层或者两层。这有益于阅读和理解。</p>
<h4 id="只做一件事"><a href="#只做一件事" class="headerlink" title="只做一件事"></a>只做一件事</h4><p>函数应该做一件事，做好这件事，只做这件事。如果函数只是做了该函数名下同一抽象层上的步骤，则函数还是只做了一件事。编写函数无非是为了把大一些的概念（换言之，函数名称）拆分为另一抽象层上的一系列步骤。这就好比书的章节目录，章节是并列的，每个章节下的小节是更细的划分，是对章节拆分为更小的概念。</p>
<h4 id="每个函数一个抽象层级"><a href="#每个函数一个抽象层级" class="headerlink" title="每个函数一个抽象层级"></a>每个函数一个抽象层级</h4><p>自顶向下读代码：向下规则。让每个函数的后面都跟着位于下一个抽象层级的函数</p>
<h4 id="switch语句"><a href="#switch语句" class="headerlink" title="switch语句"></a>switch语句</h4><p>写出短小的switch语句很难（if-else语句也是如此），但我们又无法避开switch语句，不过需要确保switch语句都埋藏在较低的抽象层级，而且永远不重复。利用多态实现这一点。</p>
<h4 id="使用描述性名称"><a href="#使用描述性名称" class="headerlink" title="使用描述性名称"></a>使用描述性名称</h4><p>函数越短小、功能越集中，就越便于取个好名字。长而具有描述性的名称，要比短而令人费解的名称好。命名方式要一致。使用与模块一脉相承的短语、名词和动词给函数命名给函数命名。</p>
<h4 id="函数参数"><a href="#函数参数" class="headerlink" title="函数参数"></a>函数参数</h4><p>最理想的函数参数是零个参数、其次是一个、然后是两个、三个等，否则你有足够特殊的理由让函数有多个参数，否则千万不要这个么做（让函数有太多的参数）。参数不易对付，参数与函数名处在不同的层级，他要求你了解目前并不特别重要的细节。换个角度，对于测试而言，参数就更叫人为难，编写测试用例覆盖各种可能值的组合非常麻烦。输出参数比输入参数还要难以理解。读函数时，我们惯以为信息通过参数输入函数，通过返回值从函数输出，我们不太期望信息通过参数输出。所以，输出参数往往让人苦思之后才恍然大悟。</p>
<h5 id="1-一元函数的普遍形式"><a href="#1-一元函数的普遍形式" class="headerlink" title="1. 一元函数的普遍形式"></a>1. 一元函数的普遍形式</h5><p>向函数中传入单个参数有两个极为普遍的理由。</p>
<pre><code>1. 问关于传入参数的问题，`bool fileExists(&quot;MyFile&quot;);`
1. 操作该参数， `InputStream fileOpen(&quot;MyFile&quot;);`这个函数做一个类型转换，将`String`转换为`InputStream`。
</code></pre>
<p>一种不太普遍但极为有用的单参数函数形式，那就是事件（event），这种形式中，有输入参数而无输出参数，该参数修改系统状态，写出这样的函数应该让代码阅读者清楚的了解这是一个事件。</p>
<h5 id="2-标识参数"><a href="#2-标识参数" class="headerlink" title="2. 标识参数"></a>2. 标识参数</h5><h5 id="3-二元函数"><a href="#3-二元函数" class="headerlink" title="3. 二元函数"></a>3. 二元函数</h5><h5 id="4-三元函数"><a href="#4-三元函数" class="headerlink" title="4. 三元函数"></a>4. 三元函数</h5><p>在写有三个参数时的三元函数时，请务必想清楚</p>
<h5 id="5-参数对象"><a href="#5-参数对象" class="headerlink" title="5. 参数对象"></a>5. 参数对象</h5><p>如果函数看起来需要两个、三个或者三个以上的函数，就说明其中一些参数应该封装成类了。例如下列两个声明：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Circle <span class="title">makeCircle</span><span class="params">(<span class="keyword">double</span> x, <span class="keyword">double</span> y, <span class="keyword">double</span> radius)</span></span>;<span class="comment">// not good</span></span><br><span class="line"><span class="function">Circle <span class="title">makeCircle</span><span class="params">(Point center, <span class="keyword">double</span> radius)</span></span>;<span class="comment">// good</span></span><br></pre></td></tr></table></figure>

<p>从参数创建对象，从而减少参数的数量。</p>
<h5 id="6-参数列表"><a href="#6-参数列表" class="headerlink" title="6. 参数列表"></a>6. 参数列表</h5><p>有时，我们想要向函数中传入数量可变，例如<code>String.format</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String.format(<span class="string">&quot;%s worked %.2f hours.&quot;</span>, name, hours);</span><br></pre></td></tr></table></figure>

<p>如果可变参数像上例那样被同等对待，就和类型为<code>List</code>的单个参数没什么两样。</p>
<h5 id="7-动词与关键字"><a href="#7-动词与关键字" class="headerlink" title="7. 动词与关键字"></a>7. 动词与关键字</h5><p>给函数取个好名字，能较好的解释函数的意图，以及参数的顺序和意图。对于一元函数，函数和参数应当形成一个非常良好的动词/名词对形式。例如<code>write(name)</code>就相当令人认同。不管这个”name”是什么，都要被<code>write</code>，所以更好的名称是<code>writeFiled(name)</code>。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">write</span><span class="params">(std::string name)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">writeFiled</span><span class="params">(std::string name)</span></span>;</span><br></pre></td></tr></table></figure>

<p>把参数的名称编码成函数名：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">assertEqual</span><span class="params">(<span class="keyword">double</span> expected, couble actual)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">assertExpectedEqualsActual</span><span class="params">(<span class="keyword">double</span> expected, couble actual)</span></span>;<span class="comment">// 减轻记忆负担</span></span><br></pre></td></tr></table></figure>

<p>这大大减轻了记忆参数顺序的负担。</p>
<h4 id="无副作用"><a href="#无副作用" class="headerlink" title="无副作用"></a>无副作用</h4><p>函数只承诺做一件事，但还是会做其他被藏起来的事情。例如，有时，它会对自己类中的变量做出未能预期的改动；有时，它会把变量搞成向函数传递的参数或是系统全局变量，无论哪种情况，都是具有破坏性的，会导致古怪的时序性耦合及顺序依赖。</p>
<h4 id="分隔指令与询问"><a href="#分隔指令与询问" class="headerlink" title="分隔指令与询问"></a>分隔指令与询问</h4><p>函数要么做什么事，要么回答什么事，但二者不可得兼。函数应该修改某对象的状态，或者返回该对象的有关信息。两样都干常会导致混乱。</p>
<h4 id="使用异常替代返回错误码"><a href="#使用异常替代返回错误码" class="headerlink" title="使用异常替代返回错误码"></a>使用异常替代返回错误码</h4><p>从指令式函数返回错误码轻微违反了指令与询问分隔的原则，它鼓励了在if语句中把指令当做表达式的使用：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="built_in">deletePages</span>(page) == E_OK)</span><br></pre></td></tr></table></figure>

<p>这容易导致更深的嵌套结构，因为有错误码返回，就要求调用者立刻处理错误。此外，如果使用异常替代返回错误码，错误处理就能从主路径代码中分离出来并得到简化。</p>
<h5 id="1-抽离try-catch代码块"><a href="#1-抽离try-catch代码块" class="headerlink" title="1. 抽离try/catch代码块"></a>1. 抽离try/catch代码块</h5><h5 id="2-错误处理就是一件事"><a href="#2-错误处理就是一件事" class="headerlink" title="2. 错误处理就是一件事"></a>2. 错误处理就是一件事</h5><h5 id="3-依赖磁铁"><a href="#3-依赖磁铁" class="headerlink" title="3. 依赖磁铁"></a>3. 依赖磁铁</h5><h4 id="别重复自己"><a href="#别重复自己" class="headerlink" title="别重复自己"></a>别重复自己</h4><p>重复可能是软件中一切邪恶的根源。许多原则与实践都是为控制消除重复而创建。</p>
<h4 id="结构化编程"><a href="#结构化编程" class="headerlink" title="结构化编程"></a>结构化编程</h4><p>每个函数、函数中的每个代码块都应该有一个入口、一个出口。遵循这些原则，这意味着在每个函数中应该只有一个<code>return</code>语句，循环中不能有<code>break</code>或者<code>continue</code>语句，而且永远不要使用<code>goto</code>语句。对于“小函数”，这些规则助益不大，在“大函数”中才有明显的好处。所以，保持函数的短小，偶尔出现<code>return</code>、<code>break</code>或者continue语句并没有坏处，甚至比单入单出原则更具有表达力，同样的，不建议使用<code>goto</code>语句。</p>
<h4 id="如何写出好的函数？"><a href="#如何写出好的函数？" class="headerlink" title="如何写出好的函数？"></a>如何写出好的函数？</h4><p>写代码和写别的东西很像，类似写文章和论文，你先想什么就写什么，然后再打磨它。出孤傲也许粗陋无序，但你坚持仔细斟酌，最终他就能达到你心目中的样子。到写函数这里，一开始都冗长而复杂，有太多的嵌套、缩进和循环、有过长的参数列表，通过不断的打磨代码、分解函数、修改名称、消除重复，最终让函数达到一个好函数的标准。</p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">代码整洁</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/15/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%E2%80%94%E2%80%94%E5%A6%82%E4%BD%95%E5%86%99%E5%A5%BD%E5%87%BD%E6%95%B0%EF%BC%9F/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-代码整洁之道（一）——命名" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/14/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E5%91%BD%E5%90%8D/">代码整洁之道（一）——命名</a>
    </h1>
  

        
        <a href="/2022/02/14/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E5%91%BD%E5%90%8D/" class="archive-article-date">
  	<time datetime="2022-02-14T12:39:07.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-14</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="代码整洁（一）——命名"><a href="#代码整洁（一）——命名" class="headerlink" title="代码整洁（一）——命名"></a>代码整洁（一）——命名</h2><h3 id="名副其实"><a href="#名副其实" class="headerlink" title="名副其实"></a>名副其实</h3><p>变量、函数或类的名称应该已经答复了所有的大问题。它应该告诉你，它为什么存在，它做什么事，应该怎么用，如果名称需要注释来补充，那就不算<strong>名副其实</strong>，例如如下所示的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> d; <span class="comment">// 消逝的时间，以日计</span></span><br></pre></td></tr></table></figure>

<p>名称<code>d</code>什么也没说明，无法体现“名副其实”的特征，我们应该选择指明计量对象和计量单位的名称：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> elapsedTimeInDays;</span><br><span class="line"><span class="keyword">int</span> daysSinceCreation;</span><br><span class="line"><span class="keyword">int</span> daysSinceModification;</span><br><span class="line"><span class="keyword">int</span> fileAgeInDays;</span><br></pre></td></tr></table></figure>

<p>选择体现本意的名称能让人更容易理解代码和修改代码。<br>再看一个例子，阅读代码段的人是否能判断下列的代码的目的何在？</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// version 1 基本是伪代码，意会其中的道理即可</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">List&lt;<span class="keyword">int</span>[]&gt; <span class="title">getThem</span><span class="params">()</span></span>&#123;</span><br><span class="line">        List&lt;<span class="keyword">int</span>[]&gt; list1 = <span class="keyword">new</span> List&lt;<span class="keyword">int</span>[]&gt;();</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span>[] x : theList)&#123;</span><br><span class="line">            <span class="keyword">if</span>(x[<span class="number">0</span>] == <span class="number">4</span>)</span><br><span class="line">                list1.<span class="built_in">add</span>(x);</span><br><span class="line">        <span class="keyword">return</span> list1;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>看到这样的代码你可能就会发出疑问四连：</p>
<ol>
<li>theList是什么类型的东西？</li>
<li>theList的零下标代表什么？</li>
<li>值4的意义是什么？</li>
<li>我该怎么使用返回的列表？</li>
</ol>
<p>假设这段代码出现在扫雷游戏中，扫雷游戏的盘面是名为theList的单元格列表，那么就将其命名为<code>gameBoard</code>，零下标代表一种状态值，状态值4代表“已标记”。只要改为有意义的名称，就可以改进出第二个版本，如下所示：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// version 2 改进命名后的代码</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">List&lt;<span class="keyword">int</span>[]&gt; <span class="title">getFlaggedCells</span><span class="params">()</span></span>&#123;</span><br><span class="line">        List&lt;<span class="keyword">int</span>[]&gt; flaggedCells = <span class="keyword">new</span> List&lt;<span class="keyword">int</span>[]&gt;();</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span>[] cell : gameBoard)&#123;</span><br><span class="line">            <span class="keyword">if</span>(cell[STATUS_VALUE] == FLAGGED)</span><br><span class="line">                flaggedCells.<span class="built_in">add</span>(cell);</span><br><span class="line">        <span class="keyword">return</span> flaggedCells;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>进一步的，不用<code>int</code>数组表示单元格，而是另写一个类<code>Cell</code>。该类包含一个成员函数<code>isFlagged</code>，于是得到最后的一个版本：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// version 3 最终版本</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">List&lt;Cell&gt; <span class="title">getFlaggedCells</span><span class="params">()</span></span>&#123;</span><br><span class="line">        List&lt;Cell&gt; flaggedCells = <span class="keyword">new</span> List&lt;Cell&gt;();</span><br><span class="line">        <span class="keyword">for</span>(Cell cell : gameBoard)&#123;</span><br><span class="line">            <span class="keyword">if</span>(cell.<span class="built_in">isFlagged</span>())</span><br><span class="line">                flaggedCells.<span class="built_in">add</span>(cell);</span><br><span class="line">        <span class="keyword">return</span> flaggedCells;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<h3 id="避免误导"><a href="#避免误导" class="headerlink" title="避免误导"></a>避免误导</h3><p>应当避免使用与本意相悖的词。例如hp、aix和sco都不应该用作变量名，它们都是UNIX平台的专用名称，可能提供误导性的信息。<br>别用<code>accountList</code>来指称一组账号，除非它真的是<code>List</code>类型。<code>List</code>一词对程序员有特殊的意义。所以用<code>accountGroup</code>或者<code>bunchOfAccount</code>，甚至直接使用<code>account</code>都会更好一些。<br>提防使用不同之处较小的名称。例如<code>XYZControllerForEfficientHandlingOfStrings</code>和<code>XYZControllerForEfficientStorgeOfStrings</code>。需要花时间进行比对，这两个词实在太相似了。</p>
<h3 id="做有意义的区分"><a href="#做有意义的区分" class="headerlink" title="做有意义的区分"></a>做有意义的区分</h3><p>以数字系列命名就是依照变量的含义命名的对立。这样的名称纯属误导——完全没有提供正确的信息，如下代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">copy</span><span class="params">(<span class="keyword">char</span> a1[], <span class="keyword">char</span> a2[])</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a1.length; i++)&#123;</span><br><span class="line">        a2[i] = a1[i];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果将参数名改为<code>source</code>和<code>destination</code>，函数就好看很多，如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">copy</span><span class="params">(<span class="keyword">char</span> source[], <span class="keyword">char</span> destination[])</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; source.length; i++)&#123;</span><br><span class="line">        destination[i] = source[i];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>废话是另一种没有意义的区分。例如这三个类：<code>Product</code>、<code>ProductInfo</code>、<code>ProductData</code>类，他们的名称不同，意思却无区别。</p>
<blockquote>
<p>注意只要体现出意义的区分，使用a或者the这样的前缀就没有问题。例将a用于局部变量，而把the用于函数参数。</p>
</blockquote>
<p>废话都是冗余的。Variable一词永远不要用于变量名中，Table一词永远不应当出现在表名中。要区分名称，就要以代码阅读者能鉴别不同之处的方式来区分。</p>
<h3 id="使用读得出来的名称"><a href="#使用读得出来的名称" class="headerlink" title="使用读得出来的名称"></a>使用读得出来的名称</h3><p>使用读得出来的名称易于开发者之间的沟通~~~，不同的开发人员读出相应的变量就可以定位相应的变量声明。</p>
<h3 id="使用可搜索的名称"><a href="#使用可搜索的名称" class="headerlink" title="使用可搜索的名称"></a>使用可搜索的名称</h3><p>使用可搜索的名称的目的是当你按下<code>ctrl+F</code>开发人员可以快速的定位变量位置。在程序中搜索a、7等数值，会出现在不同的文件不同的位置中，比较下列两段代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; <span class="number">34</span>; ++j)&#123;</span><br><span class="line">    s += (t[j] * <span class="number">4</span>) / <span class="number">5</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>和下列代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> realDaysperIdealDay = <span class="number">4</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> WORK_DAYS_PER_WEEK = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; NUMBER_OF_TASKS; j++)&#123;</span><br><span class="line">    <span class="keyword">int</span> realTaskDays = taskEstimate[j] * realDaysperIdealDay;</span><br><span class="line">    <span class="keyword">int</span> realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);</span><br><span class="line">    sum += realTaskWeeks;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第二个代码更易于搜索定位。</p>
<h3 id="避免使用编码"><a href="#避免使用编码" class="headerlink" title="避免使用编码"></a>避免使用编码</h3><h4 id="1-匈牙利语标记法（HN）"><a href="#1-匈牙利语标记法（HN）" class="headerlink" title="1. 匈牙利语标记法（HN）"></a>1. 匈牙利语标记法（HN）</h4><p>如今HN和其他类型编码形式都纯属多余，他们增加了修改变量、函数或者类的名称或者类型的难度。</p>
<h4 id="2-成员前缀"><a href="#2-成员前缀" class="headerlink" title="2. 成员前缀"></a>2. 成员前缀</h4><p>也不必用<code>m_</code>前缀来标明成员变量。（？？？这里我有些怀疑，因为看很多源码都遵循这样的命名风格），应当把类做的足够小，消除对成员前缀的需要。你应该使用某种可以高亮或用颜色标出成员的编辑环境。代码读得越多，眼中就越没有前缀。</p>
<h4 id="3-接口和实现"><a href="#3-接口和实现" class="headerlink" title="3. 接口和实现"></a>3. 接口和实现</h4><p>尝试使用不加修饰的接口类</p>
<h3 id="避免思维映射"><a href="#避免思维映射" class="headerlink" title="避免思维映射"></a>避免思维映射</h3><p>不应当让读者在脑中把你的名称翻译成他们熟知的名称。<strong>明确是王道，</strong>编写其他人能理解的代码。</p>
<h3 id="类名"><a href="#类名" class="headerlink" title="类名"></a>类名</h3><p>类名应该是名词或者名词短语，不应该使用动词。避免使用<code>Manager</code>、<code>Processor</code>、<code>Data</code>和<code>Info</code>这样的类名。</p>
<h3 id="方法名"><a href="#方法名" class="headerlink" title="方法名"></a>方法名</h3><p>方法名应该是动词或者动词短语，属性访问器、修改和判断应该加上<code>get-</code>、<code>set-</code>、<code>is-</code>的前缀。</p>
<h3 id="不要卖萌"><a href="#不要卖萌" class="headerlink" title="不要卖萌"></a>不要卖萌</h3><p>言道意到，意到言到，宁可要明确命名，绝不要为了好玩。</p>
<h3 id="每个概念对应一个词"><a href="#每个概念对应一个词" class="headerlink" title="每个概念对应一个词"></a>每个概念对应一个词</h3><p>给每个抽象概念选一个词，并一直保持这样的习惯。使用<code>fetch</code>，<code>retrieve</code>和<code>get</code>来给多个类中的同种方法命名，你怎么记得住哪个类中的那个方法呢？一以贯之的命名是天降福音。</p>
<h3 id="别用双关语"><a href="#别用双关语" class="headerlink" title="别用双关语"></a>别用双关语</h3><p>避免一个单词用于不同的目的，同一术语用于不同的概念，基本上就是双关语了。应当遵循“一词一意”。</p>
<h3 id="使用解决方案领域名称"><a href="#使用解决方案领域名称" class="headerlink" title="使用解决方案领域名称"></a>使用解决方案领域名称</h3><p>记住，只有程序员阅读你的代码，所以尽量使用那些计算机科学术语、算法名、模式名、数学术语。</p>
<h3 id="使用源自所涉问题领域的名称"><a href="#使用源自所涉问题领域的名称" class="headerlink" title="使用源自所涉问题领域的名称"></a>使用源自所涉问题领域的名称</h3><p>如果不能使用程序员所熟悉的术语来给手头的工作命名，就采用从所涉及的问题的领域而来的名称吧。</p>
<h3 id="添加有意义的语境"><a href="#添加有意义的语境" class="headerlink" title="添加有意义的语境"></a>添加有意义的语境</h3><p>多数名称难以做到自我说明，，你需要用良好命名的类、函数或者命名空间来放置名称，给代码的阅读者提供语境</p>
<h3 id="不要添加没用的语境"><a href="#不要添加没用的语境" class="headerlink" title="不要添加没用的语境"></a>不要添加没用的语境</h3><p>防止你的IDE给你过多的提示，这么做并不是一个聪明的做法。只要简短的名称足够清楚，就比长名称好。</p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">代码整洁</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/14/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E5%91%BD%E5%90%8D/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-C-设计模式（五）——Decorator模式" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/10/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%94%EF%BC%89%E2%80%94%E2%80%94Decorator%E6%A8%A1%E5%BC%8F/">C++设计模式（五）——Decorator模式</a>
    </h1>
  

        
        <a href="/2022/02/10/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%94%EF%BC%89%E2%80%94%E2%80%94Decorator%E6%A8%A1%E5%BC%8F/" class="archive-article-date">
  	<time datetime="2022-02-10T05:46:43.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-10</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="C-设计模式——装饰器模式（decorator）"><a href="#C-设计模式——装饰器模式（decorator）" class="headerlink" title="C++设计模式——装饰器模式（decorator）"></a>C++设计模式——装饰器模式（decorator）</h3><h4 id="“单一职责”模式"><a href="#“单一职责”模式" class="headerlink" title="“单一职责”模式"></a>“单一职责”模式</h4><p>​        在软件组件的设计中，如果责任划分的不清晰，使用继承得到的结果往往是随着需求的变化，<strong>子类急剧膨胀，同时充斥着重复代码，</strong>(这两点是代码的bad smell)这时候的关键是划清责任。</p>
<h4 id="“单一职责”模式常用的设计模式"><a href="#“单一职责”模式常用的设计模式" class="headerlink" title="“单一职责”模式常用的设计模式"></a>“单一职责”模式常用的设计模式</h4><ul>
<li>Decorator</li>
<li>Bridge</li>
</ul>
<h4 id="Decorator使用动机"><a href="#Decorator使用动机" class="headerlink" title="Decorator使用动机"></a>Decorator使用动机</h4><ul>
<li>在某些情况下我们可能会“过度地使用继承来扩展对象的功能”，由于继承为类型引入的静态特质，使得这种扩展方式缺乏灵活性；并且随着子类的增多（扩展功能的增多），各种子类的组合（扩展功能的组合）会导致更多子类的膨胀。</li>
<li>如何使“对象功能的扩展”能够根据需要来动态地实现？同时避免“扩展功能的增多”带来的子类膨胀问题？从而使得任何“功能扩展变化”所导致的影响将为最低  ？</li>
</ul>
<h4 id="伪代码示例——“流”IO库的设计"><a href="#伪代码示例——“流”IO库的设计" class="headerlink" title="伪代码示例——“流”IO库的设计"></a>伪代码示例——“流”IO库的设计</h4><p>​        首先写出一个“流”类的基类，然后不同的流操作（文件流、网络流等）继承该基类并重写所有的纯虚函数，所以可以写出如下的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//业务操作</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stream</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>：</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>=<span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">Stream</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//主体类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileStream</span>:</span> <span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NetworkStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写网络流</span></span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MemoryStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​        当需要考虑为该类添加加密的需求以及一些额外的扩展操作时，又需要对各种流的操作添加加密操作，加密的操作是一样的，但流的读取操作方式是不一样的。所以写出一下的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//扩展操作</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoFileStream</span> :</span><span class="keyword">public</span> FileStream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">       </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Read</span>(number);<span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Seek</span>(position);<span class="comment">//定位文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Write</span>(data);<span class="comment">//写文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoNetworkStream</span> :</span> :<span class="keyword">public</span> NetworkStream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        NetworkStream::<span class="built_in">Read</span>(number);<span class="comment">//读网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        NetworkStream::<span class="built_in">Seek</span>(position);<span class="comment">//定位网络流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        NetworkStream::<span class="built_in">Write</span>(data);<span class="comment">//写网络流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoMemoryStream</span> :</span> <span class="keyword">public</span> MemoryStream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        MemoryStream::<span class="built_in">Read</span>(number);<span class="comment">//读内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        MemoryStream::<span class="built_in">Seek</span>(position);<span class="comment">//定位内存流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        MemoryStream::<span class="built_in">Write</span>(data);<span class="comment">//写内存流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BufferedFileStream</span> :</span> <span class="keyword">public</span> FileStream&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BufferedNetworkStream</span> :</span> <span class="keyword">public</span> NetworkStream&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BufferedMemoryStream</span> :</span> <span class="keyword">public</span> MemoryStream&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoBufferedFileStream</span> :</span><span class="keyword">public</span> FileStream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        <span class="comment">//额外的缓冲操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Read</span>(number);<span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        <span class="comment">//额外的缓冲操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Seek</span>(position);<span class="comment">//定位文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        <span class="comment">//额外的缓冲操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        <span class="comment">//额外的缓冲操作...</span></span><br><span class="line">        FileStream::<span class="built_in">Write</span>(data);<span class="comment">//写文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        <span class="comment">//额外的缓冲操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Process</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//编译时装配</span></span><br><span class="line">    CryptoFileStream *fs1 = <span class="keyword">new</span> <span class="built_in">CryptoFileStream</span>();</span><br><span class="line">    BufferedFileStream *fs2 = <span class="keyword">new</span> <span class="built_in">BufferedFileStream</span>();</span><br><span class="line">    CryptoBufferedFileStream *fs3 =<span class="keyword">new</span> <span class="built_in">CryptoBufferedFileStream</span>();</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        <strong>代码分析：</strong>这样的实现方式有问题。当不同的操作出现时，子类会急剧膨胀，且对于不同的流的操作，增加部分，例如加密操作代码重复度高。继承使得子类代码急剧膨胀，随着需求的增加和不同的流操作组合，越来越多的子类会产生，所以应该适当的做出改变，一种稍好的做法可以做出如下更改：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//业务操作</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stream</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>=<span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">Stream</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//主体类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileStream</span>:</span> <span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写文件流</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NetworkStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MemoryStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//扩展操作</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoStream</span>:</span> <span class="keyword">public</span> Stream &#123;</span><br><span class="line">    </span><br><span class="line">    Stream* stream;<span class="comment">//...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">CryptoStream</span>(Stream* stm):<span class="built_in">stream</span>(stm)&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">       </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream-&gt;<span class="built_in">Read</span>(number);<span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream::<span class="built_in">Seek</span>(position);<span class="comment">//定位文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream::<span class="built_in">Write</span>(data);<span class="comment">//写文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BufferedStream</span> :</span> <span class="keyword">public</span> Stream&#123;</span><br><span class="line">    </span><br><span class="line">    Stream* stream;<span class="comment">//...</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">BufferedStream</span>(Stream* stm):<span class="built_in">stream</span>(stm)&#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Process</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//运行时装配</span></span><br><span class="line">    FileStream* s1=<span class="keyword">new</span> <span class="built_in">FileStream</span>();</span><br><span class="line">    CryptoStream* s2=<span class="keyword">new</span> <span class="built_in">CryptoStream</span>(s1);</span><br><span class="line">    </span><br><span class="line">    BufferedStream* s3=<span class="keyword">new</span> <span class="built_in">BufferedStream</span>(s1);</span><br><span class="line">    </span><br><span class="line">    BufferedStream* s4=<span class="keyword">new</span> <span class="built_in">BufferedStream</span>(s2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        <strong>代码分析：</strong>如上所示的方法中，为<code>CryptoStream</code>添加了一个成员变量<code>Stream* stream</code>，这样就可以消除其他的子类，因为代码是重复的，故只需留下一个类，利用C++语言的多态性来实现对不同流的加密，同理，其他的<code>BufferedStream</code>也用同样的思想进行简化。这里需要注意的是，<code>CryptoStream</code>继承了<code>Stream</code>类，这是为了实现多态性故意为之。写在这里，之前的问题已经的到了很大的缓解。并遵循了“宁以组合代替继承”的设计原则。这样做已经非常好了，但依旧有可改进的空间，利用decorator模式再对此代码进行改进，就可以得到如下所示的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//业务操作</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stream</span>&#123;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>=<span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">Stream</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//主体类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileStream</span>:</span> <span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写文件流</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NetworkStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写网络流</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MemoryStream</span> :</span><span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">        <span class="comment">//读内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//定位内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(<span class="keyword">char</span> data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//写内存流</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//扩展操作</span></span><br><span class="line"></span><br><span class="line">DecoratorStream: <span class="keyword">public</span> Stream&#123;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    Stream* stream;<span class="comment">//...</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">DecoratorStream</span>(Stream * stm):<span class="built_in">stream</span>(stm)&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CryptoStream</span>:</span> <span class="keyword">public</span> DecoratorStream &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">CryptoStream</span>(Stream* stm):<span class="built_in">DecoratorStream</span>(stm)&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">char</span> <span class="title">Read</span><span class="params">(<span class="keyword">int</span> number)</span></span>&#123;</span><br><span class="line">       </span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream-&gt;<span class="built_in">Read</span>(number);<span class="comment">//读文件流</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Seek</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream::<span class="built_in">Seek</span>(position);<span class="comment">//定位文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Write</span><span class="params">(byte data)</span></span>&#123;</span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">        stream::<span class="built_in">Write</span>(data);<span class="comment">//写文件流</span></span><br><span class="line">        <span class="comment">//额外的加密操作...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BufferedStream</span> :</span> <span class="keyword">public</span> DecoratorStream&#123;</span><br><span class="line">    </span><br><span class="line">    Stream* stream;<span class="comment">//...</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">BufferedStream</span>(Stream* stm):<span class="built_in">DecoratorStream</span>(stm)&#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Process</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//运行时装配</span></span><br><span class="line">    FileStream* s1=<span class="keyword">new</span> <span class="built_in">FileStream</span>();</span><br><span class="line">    </span><br><span class="line">    CryptoStream* s2=<span class="keyword">new</span> <span class="built_in">CryptoStream</span>(s1);</span><br><span class="line">    </span><br><span class="line">    BufferedStream* s3=<span class="keyword">new</span> <span class="built_in">BufferedStream</span>(s1);</span><br><span class="line">    </span><br><span class="line">    BufferedStream* s4=<span class="keyword">new</span> <span class="built_in">BufferedStream</span>(s2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="GOF对装饰器模式的定义"><a href="#GOF对装饰器模式的定义" class="headerlink" title="GOF对装饰器模式的定义"></a>GOF对装饰器模式的定义</h4><p>​        动态（组合）地给一个对象增加一些额外的职责。就增加功能而言，Decorator模式比生成子类（继承）更为灵活（消除重复代码 &amp; 减少子类个数）。（组合优于继承的典型体现）</p>
<h4 id="Decorator模式结构图"><a href="#Decorator模式结构图" class="headerlink" title="Decorator模式结构图"></a>Decorator模式结构图</h4><p><img src="https://s2.loli.net/2022/02/10/I2PTHzXmS7FgCcW.jpg" alt="decorator.jpg"></p>
<h4 id="要点总结"><a href="#要点总结" class="headerlink" title="要点总结"></a>要点总结</h4><ul>
<li>通过采用组合而非继承的手法， Decorator模式实现了在运行时动态扩展对象功能的能力，而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。</li>
<li>Decorator类在接口上表现为is-a Component的继承关系，即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系，即Decorator类又使用了另外一个Component类。一个类即继承又组合，那么这个类大概率使用了decorator的设计模式。</li>
<li>Decorator模式的目的并非解决“多子类衍生的多继承”问题，Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。  </li>
</ul>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/10/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%94%EF%BC%89%E2%80%94%E2%80%94Decorator%E6%A8%A1%E5%BC%8F/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-C-设计模式（四）——观察者模式-事件模式" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/08/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F-%E4%BA%8B%E4%BB%B6%E6%A8%A1%E5%BC%8F/">C++设计模式（四）——观察者模式/事件模式</a>
    </h1>
  

        
        <a href="/2022/02/08/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F-%E4%BA%8B%E4%BB%B6%E6%A8%A1%E5%BC%8F/" class="archive-article-date">
  	<time datetime="2022-02-08T12:45:18.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-08</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="C-设计模式（四）——观察者模式（事件模式）"><a href="#C-设计模式（四）——观察者模式（事件模式）" class="headerlink" title="C++设计模式（四）——观察者模式（事件模式）"></a>C++设计模式（四）——观察者模式（事件模式）</h3><h4 id="观察者模式应用的动机"><a href="#观察者模式应用的动机" class="headerlink" title="观察者模式应用的动机"></a>观察者模式应用的动机</h4><p>​    在软件构建过程中，我们需要为某些对象建立一种“通知依赖关系” ——一个对象（目标对象）的状态发生改变，所有的依赖对象（观察者对象）都将得到通知。如果这样的依赖关系过于紧密，将使软件不能很好地抵御变化。<br>​    使用面向对象技术，可以将这种依赖关系弱化，并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。<br>​    首先看一个文件分割器软件开发的应用场景，该场景是将一个大的文件分割成若干个小文件，并且带用户界面，根据这样的需求，就可以写出如下所示的代码：<br>​    文件FileSplitter.cpp</p>
 <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileSplitter</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	string m_filePath;<span class="comment">// 文件路径</span></span><br><span class="line">	<span class="keyword">int</span> m_fileNumber;<span class="comment">// 分割成目标文件的个数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="built_in">FileSplitter</span>(<span class="keyword">const</span> string&amp; filePath, <span class="keyword">int</span> fileNumber, ProgressBar* progressBar) :</span><br><span class="line">		<span class="built_in">m_filePath</span>(filePath), </span><br><span class="line">		<span class="built_in">m_fileNumber</span>(fileNumber)&#123;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">split</span><span class="params">()</span></span>&#123;</span><br><span class="line">	</span><br><span class="line">		<span class="comment">//1.读取大文件</span></span><br><span class="line">	</span><br><span class="line">		<span class="comment">//2.分批次向小文件中写入</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; m_fileNumber; i++)&#123;</span><br><span class="line">			<span class="comment">//...(在这里添加一些处理流程)</span></span><br><span class="line">		&#125;</span><br><span class="line">	</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    文件MainForm.cpp</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MainForm</span> :</span> <span class="keyword">public</span> Form</span><br><span class="line">&#123;</span><br><span class="line">	TextBox* txtFilePath; <span class="comment">// 分割文件的路径</span></span><br><span class="line">	TextBox* txtFileNumber; <span class="comment">// 分割文件的个数</span></span><br><span class="line">	ProgressBar* progressBar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Button1_Click</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">		string filePath = txtFilePath-&gt;<span class="built_in">getText</span>();</span><br><span class="line">		<span class="keyword">int</span> number = <span class="built_in">atoi</span>(txtFileNumber-&gt;<span class="built_in">getText</span>().<span class="built_in">c_str</span>());</span><br><span class="line"></span><br><span class="line">		<span class="function">FileSplitter <span class="title">splitter</span><span class="params">(filePath, number, progressBar)</span></span>;</span><br><span class="line"></span><br><span class="line">		splitter.<span class="built_in">split</span>();</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    由于文件尺寸大，有时分割需要很长的时间，为了更直观的看到文件分割器的分割状态，需要加一个进度条是一个很自然而然的需求，所以根据需求，可以朴素的写出以下的代码：</p>
<p>​    文件FileSplitter.cpp</p>
 <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileSplitter</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	string m_filePath;<span class="comment">// 文件路径</span></span><br><span class="line">	<span class="keyword">int</span> m_fileNumber;<span class="comment">// 分割成目标文件的个数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="built_in">FileSplitter</span>(<span class="keyword">const</span> string&amp; filePath, <span class="keyword">int</span> fileNumber, ProgressBar* progressBar) :</span><br><span class="line">		<span class="built_in">m_filePath</span>(filePath), </span><br><span class="line">		<span class="built_in">m_fileNumber</span>(fileNumber)&#123;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">split</span><span class="params">()</span></span>&#123;</span><br><span class="line">	</span><br><span class="line">		<span class="comment">//1.读取大文件</span></span><br><span class="line">	</span><br><span class="line">		<span class="comment">//2.分批次向小文件中写入</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; m_fileNumber; i++)&#123;</span><br><span class="line">			<span class="comment">//...(在这里添加一些处理流程)</span></span><br><span class="line">		&#125;</span><br><span class="line">	</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileSplitter</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	string m_filePath;</span><br><span class="line">	<span class="keyword">int</span> m_fileNumber;</span><br><span class="line">	ProgressBar* m_progressBar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="built_in">FileSplitter</span>(<span class="keyword">const</span> string&amp; filePath, <span class="keyword">int</span> fileNumber, ProgressBar* progressBar) :</span><br><span class="line">		<span class="built_in">m_filePath</span>(filePath), </span><br><span class="line">		<span class="built_in">m_fileNumber</span>(fileNumber),</span><br><span class="line">		<span class="built_in">m_progressBar</span>(progressBar)&#123;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">split</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//1.读取大文件</span></span><br><span class="line"></span><br><span class="line">		<span class="comment">//2.分批次向小文件中写入</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; m_fileNumber; i++)&#123;</span><br><span class="line">			<span class="comment">//...</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 更新进度条</span></span><br><span class="line">			<span class="keyword">float</span> progressValue = m_fileNumber;</span><br><span class="line">			progressValue = (i + <span class="number">1</span>) / progressValue;</span><br><span class="line">			m_progressBar-&gt;<span class="built_in">setValue</span>(progressValue);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    文件MainForm.cpp</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MainForm</span> :</span> <span class="keyword">public</span> Form</span><br><span class="line">&#123;</span><br><span class="line">	TextBox* txtFilePath; <span class="comment">// 分割文件的路径</span></span><br><span class="line">	TextBox* txtFileNumber; <span class="comment">// 分割文件的个数</span></span><br><span class="line">	ProgressBar* progressBar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Button1_Click</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">		string filePath = txtFilePath-&gt;<span class="built_in">getText</span>();</span><br><span class="line">		<span class="keyword">int</span> number = <span class="built_in">atoi</span>(txtFileNumber-&gt;<span class="built_in">getText</span>().<span class="built_in">c_str</span>());</span><br><span class="line"></span><br><span class="line">		<span class="function">FileSplitter <span class="title">splitter</span><span class="params">(filePath, number, progressBar)</span></span>;</span><br><span class="line"></span><br><span class="line">		splitter.<span class="built_in">split</span>();</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    代码分析：这种做法很直观，但存在一定的问题。这种做法违背了依赖倒置原则，<code>    ProgressBar* m_progressBar;</code>对编译产生依赖，设想该应用将来可能产生的变化，例如该程序被改为一个控制台应用程序，需要在终端中打印出点来记录进度，或通过打印数字的方式来表示文件分割器的文件分割状态。如何重构代码以适应这些需求的变化？</p>
<p>​    利用观察者模式修改后的代码：<br>​    文件FileSplitter.cpp</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IProgress</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">DoProgress</span><span class="params">(<span class="keyword">float</span> value)</span> </span>= <span class="number">0</span>;</span><br><span class="line">	<span class="keyword">virtual</span> ~<span class="built_in">IProgress</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileSplitter</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	string m_filePath;</span><br><span class="line">	<span class="keyword">int</span> m_fileNumber;</span><br><span class="line"></span><br><span class="line">	List&lt;IProgress*&gt;  m_iprogressList; <span class="comment">// 抽象通知机制，支持多个观察者</span></span><br><span class="line">	</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="built_in">FileSplitter</span>(<span class="keyword">const</span> string&amp; filePath, <span class="keyword">int</span> fileNumber) :</span><br><span class="line">		<span class="built_in">m_filePath</span>(filePath), </span><br><span class="line">		<span class="built_in">m_fileNumber</span>(fileNumber)&#123;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">split</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//1.读取大文件</span></span><br><span class="line"></span><br><span class="line">		<span class="comment">//2.分批次向小文件中写入</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; m_fileNumber; i++)&#123;</span><br><span class="line">			<span class="comment">//...</span></span><br><span class="line"></span><br><span class="line">			<span class="keyword">float</span> progressValue = m_fileNumber;</span><br><span class="line">			progressValue = (i + <span class="number">1</span>) / progressValue;</span><br><span class="line">			<span class="built_in">onProgress</span>(progressValue);<span class="comment">//发送通知</span></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">addIProgress</span><span class="params">(IProgress* iprogress)</span></span>&#123;</span><br><span class="line">		m_iprogressList.<span class="built_in">push_back</span>(iprogress);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">removeIProgress</span><span class="params">(IProgress* iprogress)</span></span>&#123;</span><br><span class="line">		m_iprogressList.<span class="built_in">remove</span>(iprogress);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">onProgress</span><span class="params">(<span class="keyword">float</span> value)</span></span>&#123;</span><br><span class="line">		</span><br><span class="line">		List&lt;IProgress*&gt;::iterator itor = m_iprogressList.<span class="built_in">begin</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 利用迭代器遍历List中的每一种进度条，并执行DoProgress更新进度条的值</span></span><br><span class="line">		<span class="keyword">while</span> (itor != m_iprogressList.<span class="built_in">end</span>() )</span><br><span class="line">			(*itor)-&gt;<span class="built_in">DoProgress</span>(value); <span class="comment">//更新进度条</span></span><br><span class="line">			itor++;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    文件MainForm.cpp</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MainForm</span> :</span> <span class="keyword">public</span> Form, <span class="keyword">public</span> IProgress</span><br><span class="line">&#123;</span><br><span class="line">	TextBox* txtFilePath;</span><br><span class="line">	TextBox* txtFileNumber;</span><br><span class="line"></span><br><span class="line">	ProgressBar* progressBar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Button1_Click</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">		string filePath = txtFilePath-&gt;<span class="built_in">getText</span>();</span><br><span class="line">		<span class="keyword">int</span> number = <span class="built_in">atoi</span>(txtFileNumber-&gt;<span class="built_in">getText</span>().<span class="built_in">c_str</span>());</span><br><span class="line"></span><br><span class="line">		ConsoleNotifier cn;</span><br><span class="line"></span><br><span class="line">		<span class="function">FileSplitter <span class="title">splitter</span><span class="params">(filePath, number)</span></span>;</span><br><span class="line"></span><br><span class="line">		splitter.<span class="built_in">addIProgress</span>(<span class="keyword">this</span>); <span class="comment">//订阅通知</span></span><br><span class="line">		splitter.<span class="built_in">addIProgress</span>(&amp;cn)； <span class="comment">//订阅通知</span></span><br><span class="line">		splitter.<span class="built_in">split</span>();</span><br><span class="line">		splitter.<span class="built_in">removeIProgress</span>(<span class="keyword">this</span>);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 实现IProgress中的纯虚函数</span></span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">DoProgress</span><span class="params">(<span class="keyword">float</span> value)</span></span>&#123;</span><br><span class="line">		progressBar-&gt;<span class="built_in">setValue</span>(value);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 一个Console的Observer</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConsoleNotifier</span> :</span> <span class="keyword">public</span> IProgress &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">DoProgress</span><span class="params">(<span class="keyword">float</span> value)</span></span>&#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;.&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>​    <strong>代码分析：</strong>定义一个<code>IProgress</code>的接口类，并在类<code>FileSplitter</code>类中添加了以个成员变量<code>List&lt;IProgress*&gt;  m_iprogressList;</code>，之所以使用一个list来放，是为了支持不同的进度条类型。对于<code>MainForm</code>的类而言，利用多继承的方式同时继承了<code>Form</code>和<code>IProgress</code>两个类，实<strong>际开发中并不推荐过多的使用多继承，C++推荐的多继承方式是，继承一个主的继承类和一个接口类。</strong><code>MainForm</code>类中实现了<code>IProgress</code>类的纯虚函数。这样，文件分割类没有关联任何的界面类，<code>FileSplitter</code>类的应用就可以得到更好的扩展。</p>
<h4 id="GOF对观察者模式的定义和结构"><a href="#GOF对观察者模式的定义和结构" class="headerlink" title="GOF对观察者模式的定义和结构"></a>GOF对观察者模式的定义和结构</h4><p>​    定义对象间的<strong>一种一对多（变化）的依赖关系</strong>，以便当一个对象(Subject)的状态发生改变时，所有依赖于它的对象都得到通知并自动更新。观察者模式的UML结构图如下所示：</p>
<p><img src="https://s2.loli.net/2022/02/08/eO6TB8wY2VQlGnq.jpg" alt="观察者模式.jpg"></p>
<h4 id="观察者模式要点总结"><a href="#观察者模式要点总结" class="headerlink" title="观察者模式要点总结"></a>观察者模式要点总结</h4><ul>
<li>使用面向对象的抽象，Observer模式使得我们可以独立地改变目标与观察者，从而使二者之间的依赖关系达致松耦合。</li>
<li>目标发送通知时，无需指定观察者，通知（可以携带通知信息作为参数）会自动传播。</li>
<li>观察者自己决定是否需要订阅通知，目标对象对此一无所知。</li>
<li>Observer模式是基于事件的UI框架中非常常用的设计模式，也是MVC模式的一个重要组成部分。</li>
</ul>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/08/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F-%E4%BA%8B%E4%BB%B6%E6%A8%A1%E5%BC%8F/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-C-设计模式（三）——策略模式" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/">C++设计模式（三）——策略模式</a>
    </h1>
  

        
        <a href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/" class="archive-article-date">
  	<time datetime="2022-02-07T15:47:07.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-07</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="C-设计模式（三）——策略模式"><a href="#C-设计模式（三）——策略模式" class="headerlink" title="C++设计模式（三）——策略模式"></a>C++设计模式（三）——策略模式</h3><h4 id="策略模式的动机"><a href="#策略模式的动机" class="headerlink" title="策略模式的动机"></a>策略模式的动机</h4><p>  在软件构建过程中，某些对象使用的算法可能多种多样，经常改变，如果将这些算法都编码到对象中，将会使对象变得异常复杂；而且有时候支持不使用的算法也是一个性能负担。这个性能负担主要是指在分支语句或者判断语句中，特定的场景会冗余很多的代码不被使用，这点会影响程序的性能。</p>
<h4 id="场景说明"><a href="#场景说明" class="headerlink" title="场景说明"></a>场景说明</h4><p>  现有一个程序用于计算不同国家的税，不同国家的税法有着不同的计算方式，所以一个初级的写法可以书写出如下所示的代码：</p>
  <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">  <span class="class"><span class="keyword">enum</span> <span class="title">TaxBase</span> &#123;</span></span><br><span class="line">	CN_Tax,</span><br><span class="line">	US_Tax,</span><br><span class="line">	DE_Tax,</span><br><span class="line">	FR_Tax       <span class="comment">//更改</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SalesOrder</span>&#123;</span></span><br><span class="line">    TaxBase tax;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">double</span> <span class="title">CalculateTax</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (tax == CN_Tax)&#123;</span><br><span class="line">            <span class="comment">//CN***********</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (tax == US_Tax)&#123;</span><br><span class="line">            <span class="comment">//US***********</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (tax == DE_Tax)&#123;</span><br><span class="line">            <span class="comment">//DE***********</span></span><br><span class="line">        &#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (tax == FR_Tax)&#123;  <span class="comment">//更改</span></span><br><span class="line">			<span class="comment">//...</span></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//....</span></span><br><span class="line">     &#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>  该做法乍一看并无很大问题，但对于软件设计而言，需要动态的去看软件的设计和开发。如果动态的去考察这个问题，就会有一些明显的问题，例如将来是否会加入日本等其他国家的税计算方式，要完成这样的需求，需要增加枚举类型和增加<code>if-else</code>语句。这就违背了一个设计原则：即开放封闭原则，对扩展开放对更改封闭，类模块应以扩展的方式应对变化而非修改源代码。<br>  再看另一种利用策略模式更改后的代码，代码如下所示：</p>
  <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">  </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TaxStrategy</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">Calculate</span><span class="params">(<span class="keyword">const</span> Context&amp; context)</span></span>=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">TaxStrategy</span>()&#123;&#125;<span class="comment">//C++中的基类，析构函数最好被声明为虚函数</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CNTax</span> :</span> <span class="keyword">public</span> TaxStrategy&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">Calculate</span><span class="params">(<span class="keyword">const</span> Context&amp; context)</span></span>&#123;</span><br><span class="line">        <span class="comment">//***********</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">USTax</span> :</span> <span class="keyword">public</span> TaxStrategy&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">Calculate</span><span class="params">(<span class="keyword">const</span> Context&amp; context)</span></span>&#123;</span><br><span class="line">        <span class="comment">//***********</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DETax</span> :</span> <span class="keyword">public</span> TaxStrategy&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">Calculate</span><span class="params">(<span class="keyword">const</span> Context&amp; context)</span></span>&#123;</span><br><span class="line">        <span class="comment">//***********</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//扩展</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FRTax</span> :</span> <span class="keyword">public</span> TaxStrategy&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">Calculate</span><span class="params">(<span class="keyword">const</span> Context&amp; context)</span></span>&#123;</span><br><span class="line">		<span class="comment">//.........</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SalesOrder</span>&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    TaxStrategy* strategy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">SalesOrder</span>(StrategyFactory* strategyFactory)&#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;strategy = strategyFactory-&gt;<span class="built_in">NewStrategy</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">SalesOrder</span>()&#123;</span><br><span class="line">        <span class="keyword">delete</span> <span class="keyword">this</span>-&gt;strategy;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">CalculateTax</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        <span class="function">Context <span class="title">context</span><span class="params">()</span></span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">double</span> val = </span><br><span class="line">            strategy-&gt;<span class="built_in">Calculate</span>(context); <span class="comment">//多态调用</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>  <strong>代码分析：</strong>首先定义一个基类<code>TaxStrategy</code>，该基类的析构函数被声明为一个虚函数，这是良好的习惯，否则多态的<code>delete</code>会出现问题。接下来不同国家的税法计算方式都继承该基类，并重写（override）<code>virtual double Calculate(const Context&amp; context)</code>的虚函数。最后定义一个<code>SalesOrder</code>类，该类有一个TaxStrategy指针对象（该对象一般通过工厂模式被创建出来），利用多态性完成不同税法的计算。<br>  <strong>对比两种做法：</strong>两种做法都能实现不同税法的计算，都完成了软件的业务功能。考虑一种情况，随着时间的流逝，需要增加法国的税法计算，策略模式只需增加一个新的文件里增加一个新的类，其他的代码无需改变，者遵循了开放封闭原则。代码复用指二进制级别的复用、编译单位的复用而非仅仅是代码的复制粘贴。</p>
<h4 id="GOF对策略模式的定义"><a href="#GOF对策略模式的定义" class="headerlink" title="GOF对策略模式的定义"></a>GOF对策略模式的定义</h4><p>  定义一系列算法，把它们一个个封装起来，并且使它们可互相替换（变化）。该模式<strong>使得算法可独立于使用它的客户程序(稳定)而变化（扩展，子类化）</strong>。</p>
<h4 id="策略模式结构图"><a href="#策略模式结构图" class="headerlink" title="策略模式结构图"></a>策略模式结构图</h4><p><img src="https://s2.loli.net/2022/02/07/nu7lU6aXmGbODTY.jpg" alt="策略模式.jpg"></p>
<h4 id="策略模式总结"><a href="#策略模式总结" class="headerlink" title="策略模式总结"></a>策略模式总结</h4><ul>
<li>Strategy及其子类为组件提供了一系列可重用的算法，从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。</li>
<li>Strategy模式提供了用条件判断语句以外的另一种选择，消除条件判断语句，就是在解耦合。<strong>含有许多条件判断语句的代码</strong>通常都需要Strategy模式。多个判断语句的出现是使用策略模式的标志，但也绝非有多个判断语句出现就一定要使用策略模式，例如在判断语句的次数是固定的，这种场景就不宜使用策略模式。</li>
<li>如果Strategy对象没有实例变量，那么各个上下文可以共享同一个Strategy对象，从而节省对象开销。利用判断语句或者分支语句解决问题在本例中存在太多的条件分支，而税的计算仅仅在指定的国家仅需一种方式，例如在中国，其他国家的税法就未被使用到，这些判断语句会带来性能负担。策略模式中每个国家的税法计算，也可以使用单例的方式进行改进。</li>
</ul>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-C-设计模式（二）——template-method" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94template-method/">C++设计模式（二）——template method</a>
    </h1>
  

        
        <a href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94template-method/" class="archive-article-date">
  	<time datetime="2022-02-07T07:45:46.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-07</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="C-设计模式（二）——Template-Method"><a href="#C-设计模式（二）——Template-Method" class="headerlink" title="C++设计模式（二）——Template Method"></a>C++设计模式（二）——Template Method</h3><h4 id="GOF模式分类"><a href="#GOF模式分类" class="headerlink" title="GOF模式分类"></a>GOF模式分类</h4><h5 id="从目的来看"><a href="#从目的来看" class="headerlink" title="从目的来看"></a>从目的来看</h5><ul>
<li>创建型模式：将对象的部分创建工作延迟到子类或者其他对象，从而应对需求变化为对象创建具体类型实现引来的冲击。</li>
<li>结构型模式：通过类继承或者对象组合获得更灵活的结构，从而应对需求变化为对象的结构带来的冲击。</li>
<li>行为型模式：行为型（Behavioral）模式：通过类继承或者对象组合来划分类与对象间的职责，从而应对需求变化为多个交互的对象带来的冲击。</li>
</ul>
<h5 id="从范围来看"><a href="#从范围来看" class="headerlink" title="从范围来看"></a>从范围来看</h5><ul>
<li>类模式处理类与子类的静态关系</li>
<li>对象模式处理对象间的动态关系</li>
</ul>
<h4 id="从封装变化角度对模式分类"><a href="#从封装变化角度对模式分类" class="headerlink" title="从封装变化角度对模式分类"></a>从封装变化角度对模式分类</h4><ul>
<li><p>组件协作：现代软件专业分工之后的第一个结果是“框架与应用程序的划分”，“组件协作”模式通过<strong>晚期绑定</strong>，来实现框架与应用程序之间的松耦合，是二者之间协作时常用的模式。</p>
<ul>
<li>Template Method</li>
<li>Strategy</li>
<li>Observer/Event</li>
</ul>
</li>
<li><p>单一职责</p>
<ul>
<li>Decorator</li>
<li>Bridge</li>
</ul>
</li>
<li><p>对象创建</p>
<ul>
<li>Factory Method</li>
<li>Abstract Factory</li>
<li>Phototype</li>
<li>Builder</li>
</ul>
</li>
<li><p>对象性能</p>
<ul>
<li>Singleton</li>
<li>Flyweight</li>
</ul>
</li>
<li><p>接口隔离</p>
<ul>
<li>Facade</li>
<li>Proxy</li>
<li>Mediator</li>
<li>Adapter</li>
</ul>
</li>
<li><p>状态变化</p>
<ul>
<li>Memento</li>
<li>State</li>
</ul>
</li>
<li><p>数据结构</p>
<ul>
<li>Composite</li>
<li>Iterator</li>
<li>Chain of Resposibility</li>
</ul>
</li>
<li><p>行为变化</p>
<ul>
<li>Command</li>
<li>Visitor</li>
</ul>
</li>
<li><p>领域问题</p>
<ul>
<li>Interpreter</li>
</ul>
</li>
</ul>
<h4 id="重构获得模式（Refactoring-to-Patterns）"><a href="#重构获得模式（Refactoring-to-Patterns）" class="headerlink" title="重构获得模式（Refactoring to Patterns）"></a>重构获得模式（Refactoring to Patterns）</h4><p>​    在做软件开发的过程中，不使用设计模式或者直接使用设计模式都是不太妥当的，重构到设计模式才是更为合适的做法，对比使用设计模式和不使用设计模式的优劣，对代码进行重构。</p>
<ul>
<li>面向对象设计模式是“好的面向对象设计”，所谓“好的面向对象设计”指是那些可以满足 “应对变化，提高复用”的设计 。</li>
<li>现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点，然后在变化点处应用设计模式，从而来更好地应对需求的变化”。<strong>“什么时候、什么地点应用设计模式”比“理解设计模式结构本身”更为重要。</strong></li>
<li>设计模式的应用不宜先入为主，<strong>一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。</strong>敏捷软件开发实践提倡的“Refactoring to Patterns”是目前普遍公认的最好的使用设计模式的方法。</li>
</ul>
<p><strong>推荐书籍</strong>：</p>
<ul>
<li>重构——改善既有代码的设计</li>
<li>重构与模式</li>
</ul>
<p><strong>重构的关键技法：</strong></p>
<ul>
<li>静态-&gt;动态</li>
<li>早绑定-&gt;晚绑定</li>
<li>继承-&gt;组合</li>
<li>编译时依赖-&gt;运行时依赖</li>
<li>紧耦合-&gt;松耦合</li>
</ul>
<h4 id="Template-Method"><a href="#Template-Method" class="headerlink" title="Template Method"></a>Template Method</h4><h5 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h5><p>​    在软件构建过程中，对于某一项任务，它常常有稳定的整体操作结构，但各个子步骤却有很多改变的需求，或者由于固有的原因（比如框架与应用之间的关系）而无法和任务的整体结构同时实现。如何在确定稳定操作结构的前提下，来灵活应对各个子步骤的变化或者晚期实现需求？</p>
<p>对于如下所示的代码，有类库程序开发人员和应用程序开发人员的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//程序库开发人员</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Library</span>&#123;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Step1</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Step3</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Step5</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//应用程序开发人员</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Application</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="keyword">bool</span> <span class="title">Step2</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Step4</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="function">Library <span class="title">lib</span><span class="params">()</span></span>;</span><br><span class="line">	<span class="function">Application <span class="title">app</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	lib.<span class="built_in">Step1</span>();</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (app.<span class="built_in">Step2</span>())&#123;</span><br><span class="line">		lib.<span class="built_in">Step3</span>();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++)&#123;</span><br><span class="line">		app.<span class="built_in">Step4</span>();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	lib.<span class="built_in">Step5</span>();</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上述写法是一种结构化的开发流程，开发职责和开发流程如下图所示，应用程序开发人员需要知道程序运作的流程，按照指定的顺序进行编程调用蔡锷以保证应用程序的正常运转，在win32编程中就有这样的例子，应用程序开发人员需要牢记win32程序的开发流程才可以编写应用程序：</p>
<p><img src="https://s2.loli.net/2022/02/07/KsEf9zb7MVGpR5C.jpg" alt="示意图1.jpg"></p>
<p>另一种面向对象的做法：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//程序库开发人员</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Library</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="comment">//稳定 template method</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Run</span><span class="params">()</span></span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">Step1</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">Step2</span>()) &#123; <span class="comment">//支持变化 ==&gt; 虚函数的多态调用</span></span><br><span class="line">            <span class="built_in">Step3</span>(); </span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++)&#123;</span><br><span class="line">            <span class="built_in">Step4</span>(); <span class="comment">//支持变化 ==&gt; 虚函数的多态调用</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">Step5</span>();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">	<span class="keyword">virtual</span> ~<span class="built_in">Library</span>()&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Step1</span><span class="params">()</span> </span>&#123; <span class="comment">//稳定</span></span><br><span class="line">        <span class="comment">//.....</span></span><br><span class="line">    &#125;</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Step3</span><span class="params">()</span> </span>&#123;<span class="comment">//稳定</span></span><br><span class="line">        <span class="comment">//.....</span></span><br><span class="line">    &#125;</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">Step5</span><span class="params">()</span> </span>&#123; <span class="comment">//稳定</span></span><br><span class="line">		<span class="comment">//.....</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">bool</span> <span class="title">Step2</span><span class="params">()</span> </span>= <span class="number">0</span>;<span class="comment">//变化</span></span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Step4</span><span class="params">()</span> </span>=<span class="number">0</span>; <span class="comment">//变化</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//应用程序开发人员</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Application</span> :</span> <span class="keyword">public</span> Library &#123;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">	<span class="function"><span class="keyword">virtual</span> <span class="keyword">bool</span> <span class="title">Step2</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//... 子类重写实现</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Step4</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">//... 子类重写实现</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">	    Library* pLib=<span class="keyword">new</span> <span class="built_in">Application</span>();</span><br><span class="line">	    lib-&gt;<span class="built_in">Run</span>();</span><br><span class="line"></span><br><span class="line">		<span class="keyword">delete</span> pLib;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>该做法中有两个纯虚函数需要应用程序开发人员重写，利用多态性。基类中的析构函数需要写为虚函数。示意图如下所示：</p>
<p><img src="https://s2.loli.net/2022/02/07/Vj9mMoJsvHBawxR.jpg" alt="示意图2.jpg"><br>    应用程序开发人员只需重写两个虚函数就可以实现应用程序业务流程的开发。</p>
<p>​    第一种做法是一种早绑定的写法，会加重应用程序开发人员的负担，第二种是一种晚绑定的方法。</p>
<h4 id="GOF对Template-Method的定义"><a href="#GOF对Template-Method的定义" class="headerlink" title="GOF对Template Method的定义"></a>GOF对Template Method的定义</h4><p>​    定义一个操作中的算法的骨架 (稳定)，而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。</p>
<p>​    该模式的缺点：必须有稳定的算法骨架，必须存在稳定点。（这也是所有设计模式使用前必须具备的前提条件），<strong>设计模式的主要作用就是在变化和稳定间寻找隔离点。</strong></p>
<h4 id="Template-Method结构"><a href="#Template-Method结构" class="headerlink" title="Template Method结构"></a>Template Method结构</h4><p><img src="https://s2.loli.net/2022/02/07/DtljCM1uYBeXdZ8.jpg" alt="绘图1.jpg"><br>    图中黄色部分为稳定部分，红色部分为变化部分。</p>
<h4 id="要点总结"><a href="#要点总结" class="headerlink" title="要点总结"></a>要点总结</h4><ul>
<li>Template Method模式是一种非常基础性的设计模式，在面向对象系统中有着大量的应用。它用最<strong>简洁的机制（虚函数的多态性）</strong>为很多应用程序框架提供了灵活的扩展点，是代码复用方面的基本实现结构。</li>
<li>除了可以灵活应对子步骤的变化外，“不要调用我，让我来调用你”的反向控制结构是Template Method的典型应用。</li>
<li>在具体实现方面，被Template Method调用的虚方法可以具有实现，也可以没有任何实现（抽象方法、纯虚方法），但一般推荐将它们设置为protected方法。</li>
</ul>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2022/02/07/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94template-method/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
  
    <nav id="page-nav">
      <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/">Next &amp;raquo;</a>
    </nav>
  


          </div>
        </div>
      </div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2022 Henan Yuan
    	</div>
      	<div class="footer-right">
      		<a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
      	</div>
    </div>
  </div>
</footer>
    </div>
    <script>
	var yiliaConfig = {
		mathjax: false,
		isHome: true,
		isPost: false,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false,
		toc_hide_index: true,
		root: "/",
		innerArchive: true,
		showTags: false
	}
</script>

<script>!function(t){function n(e){if(r[e])return r[e].exports;var i=r[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var r={};n.m=t,n.c=r,n.p="./",n(0)}([function(t,n,r){r(195),t.exports=r(191)},function(t,n,r){var e=r(3),i=r(52),o=r(27),u=r(28),c=r(53),f="prototype",a=function(t,n,r){var s,l,h,v,p=t&a.F,d=t&a.G,y=t&a.S,g=t&a.P,b=t&a.B,m=d?e:y?e[n]||(e[n]={}):(e[n]||{})[f],x=d?i:i[n]||(i[n]={}),w=x[f]||(x[f]={});d&&(r=n);for(s in r)l=!p&&m&&void 0!==m[s],h=(l?m:r)[s],v=b&&l?c(h,e):g&&"function"==typeof h?c(Function.call,h):h,m&&u(m,s,h,t&a.U),x[s]!=h&&o(x,s,v),g&&w[s]!=h&&(w[s]=h)};e.core=i,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,n,r){var e=r(6);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n,r){var e=r(126)("wks"),i=r(76),o=r(3).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n,r){var e=r(94),i=r(33);t.exports=function(t){return e(i(t))}},function(t,n,r){t.exports=!r(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(2),i=r(167),o=r(50),u=Object.defineProperty;n.f=r(10)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){t.exports=!r(18)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(14),i=r(22);t.exports=r(12)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(20),i=r(58),o=r(42),u=Object.defineProperty;n.f=r(12)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){var e=r(40)("wks"),i=r(23),o=r(5).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n,r){var e=r(67),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){var e=r(46);t.exports=function(t){return Object(e(t))}},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n,r){var e=r(63),i=r(34);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(21);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n,r){var e=r(11),i=r(66);t.exports=r(10)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(3),i=r(27),o=r(24),u=r(76)("src"),c="toString",f=Function[c],a=(""+f).split(c);r(52).inspectSource=function(t){return f.call(t)},(t.exports=function(t,n,r,c){var f="function"==typeof r;f&&(o(r,"name")||i(r,"name",n)),t[n]!==r&&(f&&(o(r,u)||i(r,u,t[n]?""+t[n]:a.join(String(n)))),t===e?t[n]=r:c?t[n]?t[n]=r:i(t,n,r):(delete t[n],i(t,n,r)))})(Function.prototype,c,function(){return"function"==typeof this&&this[u]||f.call(this)})},function(t,n,r){var e=r(1),i=r(4),o=r(46),u=function(t,n,r,e){var i=String(o(t)),u="<"+n;return""!==r&&(u+=" "+r+'="'+String(e).replace(/"/g,"&quot;")+'"'),u+">"+i+"</"+n+">"};t.exports=function(t,n){var r={};r[t]=n(u),e(e.P+e.F*i(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},function(t,n,r){var e=r(115),i=r(46);t.exports=function(t){return e(i(t))}},function(t,n,r){var e=r(116),i=r(66),o=r(30),u=r(50),c=r(24),f=r(167),a=Object.getOwnPropertyDescriptor;n.f=r(10)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(24),i=r(17),o=r(145)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n){t.exports={}},function(t,n){t.exports=!0},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(14).f,i=r(8),o=r(15)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(40)("keys"),i=r(23);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(5),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n,r){var e=r(21);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(36),u=r(44),c=r(14).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){n.f=r(15)},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n,r){var e=r(4);t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},function(t,n,r){var e=r(53),i=r(115),o=r(17),u=r(16),c=r(203);t.exports=function(t,n){var r=1==t,f=2==t,a=3==t,s=4==t,l=6==t,h=5==t||l,v=n||c;return function(n,c,p){for(var d,y,g=o(n),b=i(g),m=e(c,p,3),x=u(b.length),w=0,S=r?v(n,x):f?v(n,0):void 0;x>w;w++)if((h||w in b)&&(d=b[w],y=m(d,w,g),t))if(r)S[w]=y;else if(y)switch(t){case 3:return!0;case 5:return d;case 6:return w;case 2:S.push(d)}else if(s)return!1;return l?-1:a||s?s:S}}},function(t,n,r){var e=r(1),i=r(52),o=r(4);t.exports=function(t,n){var r=(i.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*o(function(){r(1)}),"Object",u)}},function(t,n,r){var e=r(6);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(91),u=r(13),c="prototype",f=function(t,n,r){var a,s,l,h=t&f.F,v=t&f.G,p=t&f.S,d=t&f.P,y=t&f.B,g=t&f.W,b=v?i:i[n]||(i[n]={}),m=b[c],x=v?e:p?e[n]:(e[n]||{})[c];v&&(r=n);for(a in r)(s=!h&&x&&void 0!==x[a])&&a in b||(l=s?x[a]:r[a],b[a]=v&&"function"!=typeof x[a]?r[a]:y&&s?o(l,e):g&&x[a]==l?function(t){var n=function(n,r,e){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,r)}return new t(n,r,e)}return t.apply(this,arguments)};return n[c]=t[c],n}(l):d&&"function"==typeof l?o(Function.call,l):l,d&&((b.virtual||(b.virtual={}))[a]=l,t&f.R&&m&&!m[a]&&u(m,a,l)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n,r){var e=r(26);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(183),i=r(1),o=r(126)("metadata"),u=o.store||(o.store=new(r(186))),c=function(t,n,r){var i=u.get(t);if(!i){if(!r)return;u.set(t,i=new e)}var o=i.get(n);if(!o){if(!r)return;i.set(n,o=new e)}return o},f=function(t,n,r){var e=c(n,r,!1);return void 0!==e&&e.has(t)},a=function(t,n,r){var e=c(n,r,!1);return void 0===e?void 0:e.get(t)},s=function(t,n,r,e){c(r,e,!0).set(t,n)},l=function(t,n){var r=c(t,n,!1),e=[];return r&&r.forEach(function(t,n){e.push(n)}),e},h=function(t){return void 0===t||"symbol"==typeof t?t:String(t)},v=function(t){i(i.S,"Reflect",t)};t.exports={store:u,map:c,has:f,get:a,set:s,keys:l,key:h,exp:v}},function(t,n,r){"use strict";if(r(10)){var e=r(69),i=r(3),o=r(4),u=r(1),c=r(127),f=r(152),a=r(53),s=r(68),l=r(66),h=r(27),v=r(73),p=r(67),d=r(16),y=r(75),g=r(50),b=r(24),m=r(180),x=r(114),w=r(6),S=r(17),_=r(137),O=r(70),E=r(32),P=r(71).f,j=r(154),F=r(76),M=r(7),A=r(48),N=r(117),T=r(146),I=r(155),k=r(80),L=r(123),R=r(74),C=r(130),D=r(160),U=r(11),W=r(31),G=U.f,B=W.f,V=i.RangeError,z=i.TypeError,q=i.Uint8Array,K="ArrayBuffer",J="Shared"+K,Y="BYTES_PER_ELEMENT",H="prototype",$=Array[H],X=f.ArrayBuffer,Q=f.DataView,Z=A(0),tt=A(2),nt=A(3),rt=A(4),et=A(5),it=A(6),ot=N(!0),ut=N(!1),ct=I.values,ft=I.keys,at=I.entries,st=$.lastIndexOf,lt=$.reduce,ht=$.reduceRight,vt=$.join,pt=$.sort,dt=$.slice,yt=$.toString,gt=$.toLocaleString,bt=M("iterator"),mt=M("toStringTag"),xt=F("typed_constructor"),wt=F("def_constructor"),St=c.CONSTR,_t=c.TYPED,Ot=c.VIEW,Et="Wrong length!",Pt=A(1,function(t,n){return Tt(T(t,t[wt]),n)}),jt=o(function(){return 1===new q(new Uint16Array([1]).buffer)[0]}),Ft=!!q&&!!q[H].set&&o(function(){new q(1).set({})}),Mt=function(t,n){if(void 0===t)throw z(Et);var r=+t,e=d(t);if(n&&!m(r,e))throw V(Et);return e},At=function(t,n){var r=p(t);if(r<0||r%n)throw V("Wrong offset!");return r},Nt=function(t){if(w(t)&&_t in t)return t;throw z(t+" is not a typed array!")},Tt=function(t,n){if(!(w(t)&&xt in t))throw z("It is not a typed array constructor!");return new t(n)},It=function(t,n){return kt(T(t,t[wt]),n)},kt=function(t,n){for(var r=0,e=n.length,i=Tt(t,e);e>r;)i[r]=n[r++];return i},Lt=function(t,n,r){G(t,n,{get:function(){return this._d[r]}})},Rt=function(t){var n,r,e,i,o,u,c=S(t),f=arguments.length,s=f>1?arguments[1]:void 0,l=void 0!==s,h=j(c);if(void 0!=h&&!_(h)){for(u=h.call(c),e=[],n=0;!(o=u.next()).done;n++)e.push(o.value);c=e}for(l&&f>2&&(s=a(s,arguments[2],2)),n=0,r=d(c.length),i=Tt(this,r);r>n;n++)i[n]=l?s(c[n],n):c[n];return i},Ct=function(){for(var t=0,n=arguments.length,r=Tt(this,n);n>t;)r[t]=arguments[t++];return r},Dt=!!q&&o(function(){gt.call(new q(1))}),Ut=function(){return gt.apply(Dt?dt.call(Nt(this)):Nt(this),arguments)},Wt={copyWithin:function(t,n){return D.call(Nt(this),t,n,arguments.length>2?arguments[2]:void 0)},every:function(t){return rt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},fill:function(t){return C.apply(Nt(this),arguments)},filter:function(t){return It(this,tt(Nt(this),t,arguments.length>1?arguments[1]:void 0))},find:function(t){return et(Nt(this),t,arguments.length>1?arguments[1]:void 0)},findIndex:function(t){return it(Nt(this),t,arguments.length>1?arguments[1]:void 0)},forEach:function(t){Z(Nt(this),t,arguments.length>1?arguments[1]:void 0)},indexOf:function(t){return ut(Nt(this),t,arguments.length>1?arguments[1]:void 0)},includes:function(t){return ot(Nt(this),t,arguments.length>1?arguments[1]:void 0)},join:function(t){return vt.apply(Nt(this),arguments)},lastIndexOf:function(t){return st.apply(Nt(this),arguments)},map:function(t){return Pt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},reduce:function(t){return lt.apply(Nt(this),arguments)},reduceRight:function(t){return ht.apply(Nt(this),arguments)},reverse:function(){for(var t,n=this,r=Nt(n).length,e=Math.floor(r/2),i=0;i<e;)t=n[i],n[i++]=n[--r],n[r]=t;return n},some:function(t){return nt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},sort:function(t){return pt.call(Nt(this),t)},subarray:function(t,n){var r=Nt(this),e=r.length,i=y(t,e);return new(T(r,r[wt]))(r.buffer,r.byteOffset+i*r.BYTES_PER_ELEMENT,d((void 0===n?e:y(n,e))-i))}},Gt=function(t,n){return It(this,dt.call(Nt(this),t,n))},Bt=function(t){Nt(this);var n=At(arguments[1],1),r=this.length,e=S(t),i=d(e.length),o=0;if(i+n>r)throw V(Et);for(;o<i;)this[n+o]=e[o++]},Vt={entries:function(){return at.call(Nt(this))},keys:function(){return ft.call(Nt(this))},values:function(){return ct.call(Nt(this))}},zt=function(t,n){return w(t)&&t[_t]&&"symbol"!=typeof n&&n in t&&String(+n)==String(n)},qt=function(t,n){return zt(t,n=g(n,!0))?l(2,t[n]):B(t,n)},Kt=function(t,n,r){return!(zt(t,n=g(n,!0))&&w(r)&&b(r,"value"))||b(r,"get")||b(r,"set")||r.configurable||b(r,"writable")&&!r.writable||b(r,"enumerable")&&!r.enumerable?G(t,n,r):(t[n]=r.value,t)};St||(W.f=qt,U.f=Kt),u(u.S+u.F*!St,"Object",{getOwnPropertyDescriptor:qt,defineProperty:Kt}),o(function(){yt.call({})})&&(yt=gt=function(){return vt.call(this)});var Jt=v({},Wt);v(Jt,Vt),h(Jt,bt,Vt.values),v(Jt,{slice:Gt,set:Bt,constructor:function(){},toString:yt,toLocaleString:Ut}),Lt(Jt,"buffer","b"),Lt(Jt,"byteOffset","o"),Lt(Jt,"byteLength","l"),Lt(Jt,"length","e"),G(Jt,mt,{get:function(){return this[_t]}}),t.exports=function(t,n,r,f){f=!!f;var a=t+(f?"Clamped":"")+"Array",l="Uint8Array"!=a,v="get"+t,p="set"+t,y=i[a],g=y||{},b=y&&E(y),m=!y||!c.ABV,S={},_=y&&y[H],j=function(t,r){var e=t._d;return e.v[v](r*n+e.o,jt)},F=function(t,r,e){var i=t._d;f&&(e=(e=Math.round(e))<0?0:e>255?255:255&e),i.v[p](r*n+i.o,e,jt)},M=function(t,n){G(t,n,{get:function(){return j(this,n)},set:function(t){return F(this,n,t)},enumerable:!0})};m?(y=r(function(t,r,e,i){s(t,y,a,"_d");var o,u,c,f,l=0,v=0;if(w(r)){if(!(r instanceof X||(f=x(r))==K||f==J))return _t in r?kt(y,r):Rt.call(y,r);o=r,v=At(e,n);var p=r.byteLength;if(void 0===i){if(p%n)throw V(Et);if((u=p-v)<0)throw V(Et)}else if((u=d(i)*n)+v>p)throw V(Et);c=u/n}else c=Mt(r,!0),u=c*n,o=new X(u);for(h(t,"_d",{b:o,o:v,l:u,e:c,v:new Q(o)});l<c;)M(t,l++)}),_=y[H]=O(Jt),h(_,"constructor",y)):L(function(t){new y(null),new y(t)},!0)||(y=r(function(t,r,e,i){s(t,y,a);var o;return w(r)?r instanceof X||(o=x(r))==K||o==J?void 0!==i?new g(r,At(e,n),i):void 0!==e?new g(r,At(e,n)):new g(r):_t in r?kt(y,r):Rt.call(y,r):new g(Mt(r,l))}),Z(b!==Function.prototype?P(g).concat(P(b)):P(g),function(t){t in y||h(y,t,g[t])}),y[H]=_,e||(_.constructor=y));var A=_[bt],N=!!A&&("values"==A.name||void 0==A.name),T=Vt.values;h(y,xt,!0),h(_,_t,a),h(_,Ot,!0),h(_,wt,y),(f?new y(1)[mt]==a:mt in _)||G(_,mt,{get:function(){return a}}),S[a]=y,u(u.G+u.W+u.F*(y!=g),S),u(u.S,a,{BYTES_PER_ELEMENT:n,from:Rt,of:Ct}),Y in _||h(_,Y,n),u(u.P,a,Wt),R(a),u(u.P+u.F*Ft,a,{set:Bt}),u(u.P+u.F*!N,a,Vt),u(u.P+u.F*(_.toString!=yt),a,{toString:yt}),u(u.P+u.F*o(function(){new y(1).slice()}),a,{slice:Gt}),u(u.P+u.F*(o(function(){return[1,2].toLocaleString()!=new y([1,2]).toLocaleString()})||!o(function(){_.toLocaleString.call([1,2])})),a,{toLocaleString:Ut}),k[a]=N?A:T,e||N||h(_,bt,T)}}else t.exports=function(){}},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n,r){var e=r(21),i=r(5).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n,r){t.exports=!r(12)&&!r(18)(function(){return 7!=Object.defineProperty(r(57)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){"use strict";var e=r(36),i=r(51),o=r(64),u=r(13),c=r(8),f=r(35),a=r(96),s=r(38),l=r(103),h=r(15)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n,r){var e=r(20),i=r(100),o=r(34),u=r(39)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(57)("iframe"),e=o.length;for(n.style.display="none",r(93).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(63),i=r(34).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(8),i=r(9),o=r(90)(!1),u=r(39)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){t.exports=r(13)},function(t,n,r){var e=r(76)("meta"),i=r(6),o=r(24),u=r(11).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(4)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},function(t,n){t.exports=!1},function(t,n,r){var e=r(2),i=r(173),o=r(133),u=r(145)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(132)("iframe"),e=o.length;for(n.style.display="none",r(135).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(175),i=r(133).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n,r){var e=r(175),i=r(133);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(28);t.exports=function(t,n,r){for(var i in n)e(t,i,n[i],r);return t}},function(t,n,r){"use strict";var e=r(3),i=r(11),o=r(10),u=r(7)("species");t.exports=function(t){var n=e[t];o&&n&&!n[u]&&i.f(n,u,{configurable:!0,get:function(){return this}})}},function(t,n,r){var e=r(67),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n,r){var e=r(33);t.exports=function(t){return Object(e(t))}},function(t,n,r){var e=r(7)("unscopables"),i=Array.prototype;void 0==i[e]&&r(27)(i,e,{}),t.exports=function(t){i[e][t]=!0}},function(t,n,r){var e=r(53),i=r(169),o=r(137),u=r(2),c=r(16),f=r(154),a={},s={},n=t.exports=function(t,n,r,l,h){var v,p,d,y,g=h?function(){return t}:f(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(o(g)){for(v=c(t.length);v>m;m++)if((y=n?b(u(p=t[m])[0],p[1]):b(t[m]))===a||y===s)return y}else for(d=g.call(t);!(p=d.next()).done;)if((y=i(d,b,p.value,n))===a||y===s)return y};n.BREAK=a,n.RETURN=s},function(t,n){t.exports={}},function(t,n,r){var e=r(11).f,i=r(24),o=r(7)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(1),i=r(46),o=r(4),u=r(150),c="["+u+"]",f="​",a=RegExp("^"+c+c+"*"),s=RegExp(c+c+"*$"),l=function(t,n,r){var i={},c=o(function(){return!!u[t]()||f[t]()!=f}),a=i[t]=c?n(h):u[t];r&&(i[r]=a),e(e.P+e.F*c,"String",i)},h=l.trim=function(t,n){return t=String(i(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(s,"")),t};t.exports=l},function(t,n,r){t.exports={default:r(86),__esModule:!0}},function(t,n,r){t.exports={default:r(87),__esModule:!0}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var i=r(84),o=e(i),u=r(83),c=e(u),f="function"==typeof c.default&&"symbol"==typeof o.default?function(t){return typeof t}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":typeof t};n.default="function"==typeof c.default&&"symbol"===f(o.default)?function(t){return void 0===t?"undefined":f(t)}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":void 0===t?"undefined":f(t)}},function(t,n,r){r(110),r(108),r(111),r(112),t.exports=r(25).Symbol},function(t,n,r){r(109),r(113),t.exports=r(44).f("iterator")},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n){t.exports=function(){}},function(t,n,r){var e=r(9),i=r(106),o=r(105);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){var e=r(88);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(19),i=r(62),o=r(37);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){t.exports=r(5).document&&document.documentElement},function(t,n,r){var e=r(56);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n,r){var e=r(56);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(60),i=r(22),o=r(38),u={};r(13)(u,r(15)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n,r){var e=r(19),i=r(9);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){var e=r(23)("meta"),i=r(21),o=r(8),u=r(14).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(18)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n,r){var e=r(14),i=r(20),o=r(19);t.exports=r(12)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(37),i=r(22),o=r(9),u=r(42),c=r(8),f=r(58),a=Object.getOwnPropertyDescriptor;n.f=r(12)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(9),i=r(61).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(8),i=r(77),o=r(39)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n,r){var e=r(41),i=r(33);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(41),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n,r){var e=r(41),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){"use strict";var e=r(89),i=r(97),o=r(35),u=r(9);t.exports=r(59)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){},function(t,n,r){"use strict";var e=r(104)(!0);r(59)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";var e=r(5),i=r(8),o=r(12),u=r(51),c=r(64),f=r(99).KEY,a=r(18),s=r(40),l=r(38),h=r(23),v=r(15),p=r(44),d=r(43),y=r(98),g=r(92),b=r(95),m=r(20),x=r(9),w=r(42),S=r(22),_=r(60),O=r(102),E=r(101),P=r(14),j=r(19),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(61).f=O.f=Z,r(37).f=X,r(62).f=tt,o&&!r(36)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(13)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){r(43)("asyncIterator")},function(t,n,r){r(43)("observable")},function(t,n,r){r(107);for(var e=r(5),i=r(13),o=r(35),u=r(15)("toStringTag"),c=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],f=0;f<5;f++){var a=c[f],s=e[a],l=s&&s.prototype;l&&!l[u]&&i(l,u,a),o[a]=o.Array}},function(t,n,r){var e=r(45),i=r(7)("toStringTag"),o="Arguments"==e(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(t){}};t.exports=function(t){var n,r,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=u(n=Object(t),i))?r:o?e(n):"Object"==(c=e(n))&&"function"==typeof n.callee?"Arguments":c}},function(t,n,r){var e=r(45);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(30),i=r(16),o=r(75);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){"use strict";var e=r(3),i=r(1),o=r(28),u=r(73),c=r(65),f=r(79),a=r(68),s=r(6),l=r(4),h=r(123),v=r(81),p=r(136);t.exports=function(t,n,r,d,y,g){var b=e[t],m=b,x=y?"set":"add",w=m&&m.prototype,S={},_=function(t){var n=w[t];o(w,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||w.forEach&&!l(function(){(new m).entries().next()}))){var O=new m,E=O[x](g?{}:-0,1)!=O,P=l(function(){O.has(1)}),j=h(function(t){new m(t)}),F=!g&&l(function(){for(var t=new m,n=5;n--;)t[x](n,n);return!t.has(-0)});j||(m=n(function(n,r){a(n,m,t);var e=p(new b,n,m);return void 0!=r&&f(r,y,e[x],e),e}),m.prototype=w,w.constructor=m),(P||F)&&(_("delete"),_("has"),y&&_("get")),(F||E)&&_(x),g&&w.clear&&delete w.clear}else m=d.getConstructor(n,t,y,x),u(m.prototype,r),c.NEED=!0;return v(m,t),S[t]=m,i(i.G+i.W+i.F*(m!=b),S),g||d.setStrong(m,t,y),m}},function(t,n,r){"use strict";var e=r(27),i=r(28),o=r(4),u=r(46),c=r(7);t.exports=function(t,n,r){var f=c(t),a=r(u,f,""[t]),s=a[0],l=a[1];o(function(){var n={};return n[f]=function(){return 7},7!=""[t](n)})&&(i(String.prototype,t,s),e(RegExp.prototype,f,2==n?function(t,n){return l.call(t,this,n)}:function(t){return l.call(t,this)}))}
},function(t,n,r){"use strict";var e=r(2);t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},function(t,n,r){var e=r(6),i=r(45),o=r(7)("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[o])?!!n:"RegExp"==i(t))}},function(t,n,r){var e=r(7)("iterator"),i=!1;try{var o=[7][e]();o.return=function(){i=!0},Array.from(o,function(){throw 2})}catch(t){}t.exports=function(t,n){if(!n&&!i)return!1;var r=!1;try{var o=[7],u=o[e]();u.next=function(){return{done:r=!0}},o[e]=function(){return u},t(o)}catch(t){}return r}},function(t,n,r){t.exports=r(69)||!r(4)(function(){var t=Math.random();__defineSetter__.call(null,t,function(){}),delete r(3)[t]})},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(3),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n,r){for(var e,i=r(3),o=r(27),u=r(76),c=u("typed_array"),f=u("view"),a=!(!i.ArrayBuffer||!i.DataView),s=a,l=0,h="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");l<9;)(e=i[h[l++]])?(o(e.prototype,c,!0),o(e.prototype,f,!0)):s=!1;t.exports={ABV:a,CONSTR:s,TYPED:c,VIEW:f}},function(t,n){"use strict";var r={versions:function(){var t=window.navigator.userAgent;return{trident:t.indexOf("Trident")>-1,presto:t.indexOf("Presto")>-1,webKit:t.indexOf("AppleWebKit")>-1,gecko:t.indexOf("Gecko")>-1&&-1==t.indexOf("KHTML"),mobile:!!t.match(/AppleWebKit.*Mobile.*/),ios:!!t.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),android:t.indexOf("Android")>-1||t.indexOf("Linux")>-1,iPhone:t.indexOf("iPhone")>-1||t.indexOf("Mac")>-1,iPad:t.indexOf("iPad")>-1,webApp:-1==t.indexOf("Safari"),weixin:-1==t.indexOf("MicroMessenger")}}()};t.exports=r},function(t,n,r){"use strict";var e=r(85),i=function(t){return t&&t.__esModule?t:{default:t}}(e),o=function(){function t(t,n,e){return n||e?String.fromCharCode(n||e):r[t]||t}function n(t){return e[t]}var r={"&quot;":'"',"&lt;":"<","&gt;":">","&amp;":"&","&nbsp;":" "},e={};for(var u in r)e[r[u]]=u;return r["&apos;"]="'",e["'"]="&#39;",{encode:function(t){return t?(""+t).replace(/['<> "&]/g,n).replace(/\r?\n/g,"<br/>").replace(/\s/g,"&nbsp;"):""},decode:function(n){return n?(""+n).replace(/<br\s*\/?>/gi,"\n").replace(/&quot;|&lt;|&gt;|&amp;|&nbsp;|&apos;|&#(\d+);|&#(\d+)/g,t).replace(/\u00a0/g," "):""},encodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},encodeBase16forJSON:function(t){if(!t)return t;t=t.replace(/[\u4E00-\u9FBF]/gi,function(t){return escape(t).replace("%u","\\u")});for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},decodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r+=2)n.push(String.fromCharCode("0x"+t.slice(r,r+2)));return n.join("")},encodeObject:function(t){if(t instanceof Array)for(var n=0,r=t.length;r>n;n++)t[n]=o.encodeObject(t[n]);else if("object"==(void 0===t?"undefined":(0,i.default)(t)))for(var e in t)t[e]=o.encodeObject(t[e]);else if("string"==typeof t)return o.encode(t);return t},loadScript:function(t){var n=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(n),n.setAttribute("src",t)},addLoadEvent:function(t){var n=window.onload;"function"!=typeof window.onload?window.onload=t:window.onload=function(){n(),t()}}}}();t.exports=o},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=function(t){for(var n=e(this),r=o(n.length),u=arguments.length,c=i(u>1?arguments[1]:void 0,r),f=u>2?arguments[2]:void 0,a=void 0===f?r:i(f,r);a>c;)n[c++]=t;return n}},function(t,n,r){"use strict";var e=r(11),i=r(66);t.exports=function(t,n,r){n in t?e.f(t,n,i(0,r)):t[n]=r}},function(t,n,r){var e=r(6),i=r(3).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n,r){var e=r(7)("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(t){}}return!0}},function(t,n,r){t.exports=r(3).document&&document.documentElement},function(t,n,r){var e=r(6),i=r(144).set;t.exports=function(t,n,r){var o,u=n.constructor;return u!==r&&"function"==typeof u&&(o=u.prototype)!==r.prototype&&e(o)&&i&&i(t,o),t}},function(t,n,r){var e=r(80),i=r(7)("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||o[i]===t)}},function(t,n,r){var e=r(45);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(70),i=r(66),o=r(81),u={};r(27)(u,r(7)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n,r){"use strict";var e=r(69),i=r(1),o=r(28),u=r(27),c=r(24),f=r(80),a=r(139),s=r(81),l=r(32),h=r(7)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},function(t,n,r){var e=r(3),i=r(151).set,o=e.MutationObserver||e.WebKitMutationObserver,u=e.process,c=e.Promise,f="process"==r(45)(u);t.exports=function(){var t,n,r,a=function(){var e,i;for(f&&(e=u.domain)&&e.exit();t;){i=t.fn,t=t.next;try{i()}catch(e){throw t?r():n=void 0,e}}n=void 0,e&&e.enter()};if(f)r=function(){u.nextTick(a)};else if(o){var s=!0,l=document.createTextNode("");new o(a).observe(l,{characterData:!0}),r=function(){l.data=s=!s}}else if(c&&c.resolve){var h=c.resolve();r=function(){h.then(a)}}else r=function(){i.call(e,a)};return function(e){var i={fn:e,next:void 0};n&&(n.next=i),t||(t=i,r()),n=i}}},function(t,n,r){var e=r(6),i=r(2),o=function(t,n){if(i(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{e=r(53)(Function.call,r(31).f(Object.prototype,"__proto__").set,2),e(t,[]),n=!(t instanceof Array)}catch(t){n=!0}return function(t,r){return o(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:o}},function(t,n,r){var e=r(126)("keys"),i=r(76);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(2),i=r(26),o=r(7)("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||void 0==(r=e(u)[o])?n:i(r)}},function(t,n,r){var e=r(67),i=r(46);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(122),i=r(46);t.exports=function(t,n,r){if(e(n))throw TypeError("String#"+r+" doesn't accept regex!");return String(i(t))}},function(t,n,r){"use strict";var e=r(67),i=r(46);t.exports=function(t){var n=String(i(this)),r="",o=e(t);if(o<0||o==1/0)throw RangeError("Count can't be negative");for(;o>0;(o>>>=1)&&(n+=n))1&o&&(r+=n);return r}},function(t,n){t.exports="\t\n\v\f\r   ᠎             　\u2028\u2029\ufeff"},function(t,n,r){var e,i,o,u=r(53),c=r(121),f=r(135),a=r(132),s=r(3),l=s.process,h=s.setImmediate,v=s.clearImmediate,p=s.MessageChannel,d=0,y={},g="onreadystatechange",b=function(){var t=+this;if(y.hasOwnProperty(t)){var n=y[t];delete y[t],n()}},m=function(t){b.call(t.data)};h&&v||(h=function(t){for(var n=[],r=1;arguments.length>r;)n.push(arguments[r++]);return y[++d]=function(){c("function"==typeof t?t:Function(t),n)},e(d),d},v=function(t){delete y[t]},"process"==r(45)(l)?e=function(t){l.nextTick(u(b,t,1))}:p?(i=new p,o=i.port2,i.port1.onmessage=m,e=u(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(e=function(t){s.postMessage(t+"","*")},s.addEventListener("message",m,!1)):e=g in a("script")?function(t){f.appendChild(a("script"))[g]=function(){f.removeChild(this),b.call(t)}}:function(t){setTimeout(u(b,t,1),0)}),t.exports={set:h,clear:v}},function(t,n,r){"use strict";var e=r(3),i=r(10),o=r(69),u=r(127),c=r(27),f=r(73),a=r(4),s=r(68),l=r(67),h=r(16),v=r(71).f,p=r(11).f,d=r(130),y=r(81),g="ArrayBuffer",b="DataView",m="prototype",x="Wrong length!",w="Wrong index!",S=e[g],_=e[b],O=e.Math,E=e.RangeError,P=e.Infinity,j=S,F=O.abs,M=O.pow,A=O.floor,N=O.log,T=O.LN2,I="buffer",k="byteLength",L="byteOffset",R=i?"_b":I,C=i?"_l":k,D=i?"_o":L,U=function(t,n,r){var e,i,o,u=Array(r),c=8*r-n-1,f=(1<<c)-1,a=f>>1,s=23===n?M(2,-24)-M(2,-77):0,l=0,h=t<0||0===t&&1/t<0?1:0;for(t=F(t),t!=t||t===P?(i=t!=t?1:0,e=f):(e=A(N(t)/T),t*(o=M(2,-e))<1&&(e--,o*=2),t+=e+a>=1?s/o:s*M(2,1-a),t*o>=2&&(e++,o/=2),e+a>=f?(i=0,e=f):e+a>=1?(i=(t*o-1)*M(2,n),e+=a):(i=t*M(2,a-1)*M(2,n),e=0));n>=8;u[l++]=255&i,i/=256,n-=8);for(e=e<<n|i,c+=n;c>0;u[l++]=255&e,e/=256,c-=8);return u[--l]|=128*h,u},W=function(t,n,r){var e,i=8*r-n-1,o=(1<<i)-1,u=o>>1,c=i-7,f=r-1,a=t[f--],s=127&a;for(a>>=7;c>0;s=256*s+t[f],f--,c-=8);for(e=s&(1<<-c)-1,s>>=-c,c+=n;c>0;e=256*e+t[f],f--,c-=8);if(0===s)s=1-u;else{if(s===o)return e?NaN:a?-P:P;e+=M(2,n),s-=u}return(a?-1:1)*e*M(2,s-n)},G=function(t){return t[3]<<24|t[2]<<16|t[1]<<8|t[0]},B=function(t){return[255&t]},V=function(t){return[255&t,t>>8&255]},z=function(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]},q=function(t){return U(t,52,8)},K=function(t){return U(t,23,4)},J=function(t,n,r){p(t[m],n,{get:function(){return this[r]}})},Y=function(t,n,r,e){var i=+r,o=l(i);if(i!=o||o<0||o+n>t[C])throw E(w);var u=t[R]._b,c=o+t[D],f=u.slice(c,c+n);return e?f:f.reverse()},H=function(t,n,r,e,i,o){var u=+r,c=l(u);if(u!=c||c<0||c+n>t[C])throw E(w);for(var f=t[R]._b,a=c+t[D],s=e(+i),h=0;h<n;h++)f[a+h]=s[o?h:n-h-1]},$=function(t,n){s(t,S,g);var r=+n,e=h(r);if(r!=e)throw E(x);return e};if(u.ABV){if(!a(function(){new S})||!a(function(){new S(.5)})){S=function(t){return new j($(this,t))};for(var X,Q=S[m]=j[m],Z=v(j),tt=0;Z.length>tt;)(X=Z[tt++])in S||c(S,X,j[X]);o||(Q.constructor=S)}var nt=new _(new S(2)),rt=_[m].setInt8;nt.setInt8(0,2147483648),nt.setInt8(1,2147483649),!nt.getInt8(0)&&nt.getInt8(1)||f(_[m],{setInt8:function(t,n){rt.call(this,t,n<<24>>24)},setUint8:function(t,n){rt.call(this,t,n<<24>>24)}},!0)}else S=function(t){var n=$(this,t);this._b=d.call(Array(n),0),this[C]=n},_=function(t,n,r){s(this,_,b),s(t,S,b);var e=t[C],i=l(n);if(i<0||i>e)throw E("Wrong offset!");if(r=void 0===r?e-i:h(r),i+r>e)throw E(x);this[R]=t,this[D]=i,this[C]=r},i&&(J(S,k,"_l"),J(_,I,"_b"),J(_,k,"_l"),J(_,L,"_o")),f(_[m],{getInt8:function(t){return Y(this,1,t)[0]<<24>>24},getUint8:function(t){return Y(this,1,t)[0]},getInt16:function(t){var n=Y(this,2,t,arguments[1]);return(n[1]<<8|n[0])<<16>>16},getUint16:function(t){var n=Y(this,2,t,arguments[1]);return n[1]<<8|n[0]},getInt32:function(t){return G(Y(this,4,t,arguments[1]))},getUint32:function(t){return G(Y(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return W(Y(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return W(Y(this,8,t,arguments[1]),52,8)},setInt8:function(t,n){H(this,1,t,B,n)},setUint8:function(t,n){H(this,1,t,B,n)},setInt16:function(t,n){H(this,2,t,V,n,arguments[2])},setUint16:function(t,n){H(this,2,t,V,n,arguments[2])},setInt32:function(t,n){H(this,4,t,z,n,arguments[2])},setUint32:function(t,n){H(this,4,t,z,n,arguments[2])},setFloat32:function(t,n){H(this,4,t,K,n,arguments[2])},setFloat64:function(t,n){H(this,8,t,q,n,arguments[2])}});y(S,g),y(_,b),c(_[m],u.VIEW,!0),n[g]=S,n[b]=_},function(t,n,r){var e=r(3),i=r(52),o=r(69),u=r(182),c=r(11).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){var e=r(114),i=r(7)("iterator"),o=r(80);t.exports=r(52).getIteratorMethod=function(t){if(void 0!=t)return t[i]||t["@@iterator"]||o[e(t)]}},function(t,n,r){"use strict";var e=r(78),i=r(170),o=r(80),u=r(30);t.exports=r(140)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){function r(t,n){t.classList?t.classList.add(n):t.className+=" "+n}t.exports=r},function(t,n){function r(t,n){if(t.classList)t.classList.remove(n);else{var r=new RegExp("(^|\\b)"+n.split(" ").join("|")+"(\\b|$)","gi");t.className=t.className.replace(r," ")}}t.exports=r},function(t,n){function r(){throw new Error("setTimeout has not been defined")}function e(){throw new Error("clearTimeout has not been defined")}function i(t){if(s===setTimeout)return setTimeout(t,0);if((s===r||!s)&&setTimeout)return s=setTimeout,setTimeout(t,0);try{return s(t,0)}catch(n){try{return s.call(null,t,0)}catch(n){return s.call(this,t,0)}}}function o(t){if(l===clearTimeout)return clearTimeout(t);if((l===e||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(t);try{return l(t)}catch(n){try{return l.call(null,t)}catch(n){return l.call(this,t)}}}function u(){d&&v&&(d=!1,v.length?p=v.concat(p):y=-1,p.length&&c())}function c(){if(!d){var t=i(u);d=!0;for(var n=p.length;n;){for(v=p,p=[];++y<n;)v&&v[y].run();y=-1,n=p.length}v=null,d=!1,o(t)}}function f(t,n){this.fun=t,this.array=n}function a(){}var s,l,h=t.exports={};!function(){try{s="function"==typeof setTimeout?setTimeout:r}catch(t){s=r}try{l="function"==typeof clearTimeout?clearTimeout:e}catch(t){l=e}}();var v,p=[],d=!1,y=-1;h.nextTick=function(t){var n=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)n[r-1]=arguments[r];p.push(new f(t,n)),1!==p.length||d||i(c)},f.prototype.run=function(){this.fun.apply(null,this.array)},h.title="browser",h.browser=!0,h.env={},h.argv=[],h.version="",h.versions={},h.on=a,h.addListener=a,h.once=a,h.off=a,h.removeListener=a,h.removeAllListeners=a,h.emit=a,h.prependListener=a,h.prependOnceListener=a,h.listeners=function(t){return[]},h.binding=function(t){throw new Error("process.binding is not supported")},h.cwd=function(){return"/"},h.chdir=function(t){throw new Error("process.chdir is not supported")},h.umask=function(){return 0}},function(t,n,r){var e=r(45);t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=[].copyWithin||function(t,n){var r=e(this),u=o(r.length),c=i(t,u),f=i(n,u),a=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===a?u:i(a,u))-f,u-c),l=1;for(f<c&&c<f+s&&(l=-1,f+=s-1,c+=s-1);s-- >0;)f in r?r[c]=r[f]:delete r[c],c+=l,f+=l;return r}},function(t,n,r){var e=r(79);t.exports=function(t,n){var r=[];return e(t,!1,r.push,r,n),r}},function(t,n,r){var e=r(26),i=r(17),o=r(115),u=r(16);t.exports=function(t,n,r,c,f){e(n);var a=i(t),s=o(a),l=u(a.length),h=f?l-1:0,v=f?-1:1;if(r<2)for(;;){if(h in s){c=s[h],h+=v;break}if(h+=v,f?h<0:l<=h)throw TypeError("Reduce of empty array with no initial value")}for(;f?h>=0:l>h;h+=v)h in s&&(c=n(c,s[h],h,a));return c}},function(t,n,r){"use strict";var e=r(26),i=r(6),o=r(121),u=[].slice,c={},f=function(t,n,r){if(!(n in c)){for(var e=[],i=0;i<n;i++)e[i]="a["+i+"]";c[n]=Function("F,a","return new F("+e.join(",")+")")}return c[n](t,r)};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),c=function(){var e=r.concat(u.call(arguments));return this instanceof c?f(n,e.length,e):o(n,e,t)};return i(n.prototype)&&(c.prototype=n.prototype),c}},function(t,n,r){"use strict";var e=r(11).f,i=r(70),o=r(73),u=r(53),c=r(68),f=r(46),a=r(79),s=r(140),l=r(170),h=r(74),v=r(10),p=r(65).fastKey,d=v?"_s":"size",y=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,s){var l=t(function(t,e){c(t,l,n,"_i"),t._i=i(null),t._f=void 0,t._l=void 0,t[d]=0,void 0!=e&&a(e,r,t[s],t)});return o(l.prototype,{clear:function(){for(var t=this,n=t._i,r=t._f;r;r=r.n)r.r=!0,r.p&&(r.p=r.p.n=void 0),delete n[r.i];t._f=t._l=void 0,t[d]=0},delete:function(t){var n=this,r=y(n,t);if(r){var e=r.n,i=r.p;delete n._i[r.i],r.r=!0,i&&(i.n=e),e&&(e.p=i),n._f==r&&(n._f=e),n._l==r&&(n._l=i),n[d]--}return!!r},forEach:function(t){c(this,l,"forEach");for(var n,r=u(t,arguments.length>1?arguments[1]:void 0,3);n=n?n.n:this._f;)for(r(n.v,n.k,this);n&&n.r;)n=n.p},has:function(t){return!!y(this,t)}}),v&&e(l.prototype,"size",{get:function(){return f(this[d])}}),l},def:function(t,n,r){var e,i,o=y(t,n);return o?o.v=r:(t._l=o={i:i=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=o),e&&(e.n=o),t[d]++,"F"!==i&&(t._i[i]=o)),t},getEntry:y,setStrong:function(t,n,r){s(t,n,function(t,n){this._t=t,this._k=n,this._l=void 0},function(){for(var t=this,n=t._k,r=t._l;r&&r.r;)r=r.p;return t._t&&(t._l=r=r?r.n:t._t._f)?"keys"==n?l(0,r.k):"values"==n?l(0,r.v):l(0,[r.k,r.v]):(t._t=void 0,l(1))},r?"entries":"values",!r,!0),h(n)}}},function(t,n,r){var e=r(114),i=r(161);t.exports=function(t){return function(){if(e(this)!=t)throw TypeError(t+"#toJSON isn't generic");return i(this)}}},function(t,n,r){"use strict";var e=r(73),i=r(65).getWeak,o=r(2),u=r(6),c=r(68),f=r(79),a=r(48),s=r(24),l=a(5),h=a(6),v=0,p=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},y=function(t,n){return l(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=y(this,t);if(n)return n[1]},has:function(t){return!!y(this,t)},set:function(t,n){var r=y(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=h(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,o){var a=t(function(t,e){c(t,a,n,"_i"),t._i=v++,t._l=void 0,void 0!=e&&f(e,r,t[o],t)});return e(a.prototype,{delete:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).delete(t):n&&s(n,this._i)&&delete n[this._i]},has:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).has(t):n&&s(n,this._i)}}),a},def:function(t,n,r){var e=i(o(n),!0);return!0===e?p(t).set(n,r):e[t._i]=r,t},ufstore:p}},function(t,n,r){t.exports=!r(10)&&!r(4)(function(){return 7!=Object.defineProperty(r(132)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(6),i=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&i(t)===t}},function(t,n,r){var e=r(2);t.exports=function(t,n,r,i){try{return i?n(e(r)[0],r[1]):n(r)}catch(n){var o=t.return;throw void 0!==o&&e(o.call(t)),n}}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},function(t,n,r){"use strict";var e=r(72),i=r(125),o=r(116),u=r(17),c=r(115),f=Object.assign;t.exports=!f||r(4)(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=f({},t)[r]||Object.keys(f({},n)).join("")!=e})?function(t,n){for(var r=u(t),f=arguments.length,a=1,s=i.f,l=o.f;f>a;)for(var h,v=c(arguments[a++]),p=s?e(v).concat(s(v)):e(v),d=p.length,y=0;d>y;)l.call(v,h=p[y++])&&(r[h]=v[h]);return r}:f},function(t,n,r){var e=r(11),i=r(2),o=r(72);t.exports=r(10)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(30),i=r(71).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(24),i=r(30),o=r(117)(!1),u=r(145)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){var e=r(72),i=r(30),o=r(116).f;t.exports=function(t){return function(n){for(var r,u=i(n),c=e(u),f=c.length,a=0,s=[];f>a;)o.call(u,r=c[a++])&&s.push(t?[r,u[r]]:u[r]);return s}}},function(t,n,r){var e=r(71),i=r(125),o=r(2),u=r(3).Reflect;t.exports=u&&u.ownKeys||function(t){var n=e.f(o(t)),r=i.f;return r?n.concat(r(t)):n}},function(t,n,r){var e=r(3).parseFloat,i=r(82).trim;t.exports=1/e(r(150)+"-0")!=-1/0?function(t){var n=i(String(t),3),r=e(n);return 0===r&&"-"==n.charAt(0)?-0:r}:e},function(t,n,r){var e=r(3).parseInt,i=r(82).trim,o=r(150),u=/^[\-+]?0[xX]/;t.exports=8!==e(o+"08")||22!==e(o+"0x16")?function(t,n){var r=i(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},function(t,n,r){var e=r(16),i=r(149),o=r(46);t.exports=function(t,n,r,u){var c=String(o(t)),f=c.length,a=void 0===r?" ":String(r),s=e(n);if(s<=f||""==a)return c;var l=s-f,h=i.call(a,Math.ceil(l/a.length));return h.length>l&&(h=h.slice(0,l)),u?h+c:c+h}},function(t,n,r){n.f=r(7)},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Map",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(this,t);return n&&n.v},set:function(t,n){return e.def(this,0===t?0:t,n)}},e,!0)},function(t,n,r){r(10)&&"g"!=/./g.flags&&r(11).f(RegExp.prototype,"flags",{configurable:!0,get:r(120)})},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t=0===t?0:t,t)}},e)},function(t,n,r){"use strict";var e,i=r(48)(0),o=r(28),u=r(65),c=r(172),f=r(166),a=r(6),s=u.getWeak,l=Object.isExtensible,h=f.ufstore,v={},p=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},d={get:function(t){if(a(t)){var n=s(t);return!0===n?h(this).get(t):n?n[this._i]:void 0}},set:function(t,n){return f.def(this,t,n)}},y=t.exports=r(118)("WeakMap",p,d,f,!0,!0);7!=(new y).set((Object.freeze||Object)(v),7).get(v)&&(e=f.getConstructor(p),c(e.prototype,d),u.NEED=!0,i(["delete","has","get","set"],function(t){var n=y.prototype,r=n[t];o(n,t,function(n,i){if(a(n)&&!l(n)){this._f||(this._f=new e);var o=this._f[t](n,i);return"set"==t?this:o}return r.call(this,n,i)})}))},,,,function(t,n){"use strict";function r(){var t=document.querySelector("#page-nav");if(t&&!document.querySelector("#page-nav .extend.prev")&&(t.innerHTML='<a class="extend prev disabled" rel="prev">&laquo; Prev</a>'+t.innerHTML),t&&!document.querySelector("#page-nav .extend.next")&&(t.innerHTML=t.innerHTML+'<a class="extend next disabled" rel="next">Next &raquo;</a>'),yiliaConfig&&yiliaConfig.open_in_new){document.querySelectorAll(".article-entry a:not(.article-more-a)").forEach(function(t){var n=t.getAttribute("target");n&&""!==n||t.setAttribute("target","_blank")})}if(yiliaConfig&&yiliaConfig.toc_hide_index){document.querySelectorAll(".toc-number").forEach(function(t){t.style.display="none"})}var n=document.querySelector("#js-aboutme");n&&0!==n.length&&(n.innerHTML=n.innerText)}t.exports={init:r}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}function i(t,n){var r=/\/|index.html/g;return t.replace(r,"")===n.replace(r,"")}function o(){for(var t=document.querySelectorAll(".js-header-menu li a"),n=window.location.pathname,r=0,e=t.length;r<e;r++){var o=t[r];i(n,o.getAttribute("href"))&&(0,h.default)(o,"active")}}function u(t){for(var n=t.offsetLeft,r=t.offsetParent;null!==r;)n+=r.offsetLeft,r=r.offsetParent;return n}function c(t){for(var n=t.offsetTop,r=t.offsetParent;null!==r;)n+=r.offsetTop,r=r.offsetParent;return n}function f(t,n,r,e,i){var o=u(t),f=c(t)-n;if(f-r<=i){var a=t.$newDom;a||(a=t.cloneNode(!0),(0,d.default)(t,a),t.$newDom=a,a.style.position="fixed",a.style.top=(r||f)+"px",a.style.left=o+"px",a.style.zIndex=e||2,a.style.width="100%",a.style.color="#fff"),a.style.visibility="visible",t.style.visibility="hidden"}else{t.style.visibility="visible";var s=t.$newDom;s&&(s.style.visibility="hidden")}}function a(){var t=document.querySelector(".js-overlay"),n=document.querySelector(".js-header-menu");f(t,document.body.scrollTop,-63,2,0),f(n,document.body.scrollTop,1,3,0)}function s(){document.querySelector("#container").addEventListener("scroll",function(t){a()}),window.addEventListener("scroll",function(t){a()}),a()}var l=r(156),h=e(l),v=r(157),p=(e(v),r(382)),d=e(p),y=r(128),g=e(y),b=r(190),m=e(b),x=r(129);(function(){g.default.versions.mobile&&window.screen.width<800&&(o(),s())})(),(0,x.addLoadEvent)(function(){m.default.init()}),t.exports={}},,,,function(t,n,r){(function(t){"use strict";function n(t,n,r){t[n]||Object[e](t,n,{writable:!0,configurable:!0,value:r})}if(r(381),r(391),r(198),t._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");t._babelPolyfill=!0;var e="defineProperty";n(String.prototype,"padLeft","".padStart),n(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function(t){[][t]&&n(Array,t,Function.call.bind([][t]))})}).call(n,function(){return this}())},,,function(t,n,r){r(210),t.exports=r(52).RegExp.escape},,,,function(t,n,r){var e=r(6),i=r(138),o=r(7)("species");t.exports=function(t){var n;return i(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!i(n.prototype)||(n=void 0),e(n)&&null===(n=n[o])&&(n=void 0)),void 0===n?Array:n}},function(t,n,r){var e=r(202);t.exports=function(t,n){return new(e(t))(n)}},function(t,n,r){"use strict";var e=r(2),i=r(50),o="number";t.exports=function(t){if("string"!==t&&t!==o&&"default"!==t)throw TypeError("Incorrect hint");return i(e(this),t!=o)}},function(t,n,r){var e=r(72),i=r(125),o=r(116);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){var e=r(72),i=r(30);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){"use strict";var e=r(208),i=r(121),o=r(26);t.exports=function(){for(var t=o(this),n=arguments.length,r=Array(n),u=0,c=e._,f=!1;n>u;)(r[u]=arguments[u++])===c&&(f=!0);return function(){var e,o=this,u=arguments.length,a=0,s=0;if(!f&&!u)return i(t,r,o);if(e=r.slice(),f)for(;n>a;a++)e[a]===c&&(e[a]=arguments[s++]);for(;u>s;)e.push(arguments[s++]);return i(t,e,o)}}},function(t,n,r){t.exports=r(3)},function(t,n){t.exports=function(t,n){var r=n===Object(n)?function(t){return n[t]}:n;return function(n){return String(n).replace(t,r)}}},function(t,n,r){var e=r(1),i=r(209)(/[\\^$*+?.()|[\]{}]/g,"\\$&");e(e.S,"RegExp",{escape:function(t){return i(t)}})},function(t,n,r){var e=r(1);e(e.P,"Array",{copyWithin:r(160)}),r(78)("copyWithin")},function(t,n,r){"use strict";var e=r(1),i=r(48)(4);e(e.P+e.F*!r(47)([].every,!0),"Array",{every:function(t){return i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.P,"Array",{fill:r(130)}),r(78)("fill")},function(t,n,r){"use strict";var e=r(1),i=r(48)(2);e(e.P+e.F*!r(47)([].filter,!0),"Array",{filter:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(6),o="findIndex",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{findIndex:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(5),o="find",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{find:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(0),o=r(47)([].forEach,!0);e(e.P+e.F*!o,"Array",{forEach:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(53),i=r(1),o=r(17),u=r(169),c=r(137),f=r(16),a=r(131),s=r(154);i(i.S+i.F*!r(123)(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,i,l,h=o(t),v="function"==typeof this?this:Array,p=arguments.length,d=p>1?arguments[1]:void 0,y=void 0!==d,g=0,b=s(h);if(y&&(d=e(d,p>2?arguments[2]:void 0,2)),void 0==b||v==Array&&c(b))for(n=f(h.length),r=new v(n);n>g;g++)a(r,g,y?d(h[g],g):h[g]);else for(l=b.call(h),r=new v;!(i=l.next()).done;g++)a(r,g,y?u(l,d,[i.value,g],!0):i.value);return r.length=g,r}})},function(t,n,r){"use strict";var e=r(1),i=r(117)(!1),o=[].indexOf,u=!!o&&1/[1].indexOf(1,-0)<0;e(e.P+e.F*(u||!r(47)(o)),"Array",{indexOf:function(t){return u?o.apply(this,arguments)||0:i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.S,"Array",{isArray:r(138)})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=[].join;e(e.P+e.F*(r(115)!=Object||!r(47)(o)),"Array",{join:function(t){return o.call(i(this),void 0===t?",":t)}})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=r(67),u=r(16),c=[].lastIndexOf,f=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(f||!r(47)(c)),"Array",{lastIndexOf:function(t){if(f)return c.apply(this,arguments)||0;var n=i(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,o(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(1);e(e.P+e.F*!r(47)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(131);e(e.S+e.F*r(4)(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)i(r,t,arguments[t++]);return r.length=n,r}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduceRight,!0),"Array",{reduceRight:function(t){return i(this,t,arguments.length,arguments[1],!0)}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduce,!0),"Array",{reduce:function(t){return i(this,t,arguments.length,arguments[1],!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(135),o=r(45),u=r(75),c=r(16),f=[].slice;e(e.P+e.F*r(4)(function(){i&&f.call(i)}),"Array",{slice:function(t,n){var r=c(this.length),e=o(this);if(n=void 0===n?r:n,"Array"==e)return f.call(this,t,n);for(var i=u(t,r),a=u(n,r),s=c(a-i),l=Array(s),h=0;h<s;h++)l[h]="String"==e?this.charAt(i+h):this[i+h];return l}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(3);e(e.P+e.F*!r(47)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(26),o=r(17),u=r(4),c=[].sort,f=[1,2,3];e(e.P+e.F*(u(function(){f.sort(void 0)})||!u(function(){f.sort(null)})||!r(47)(c)),"Array",{sort:function(t){return void 0===t?c.call(o(this)):c.call(o(this),i(t))}})},function(t,n,r){r(74)("Array")},function(t,n,r){var e=r(1);e(e.S,"Date",{now:function(){return(new Date).getTime()}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=Date.prototype.getTime,u=function(t){return t>9?t:"0"+t};e(e.P+e.F*(i(function(){return"0385-07-25T07:06:39.999Z"!=new Date(-5e13-1).toISOString()})||!i(function(){new Date(NaN).toISOString()})),"Date",{toISOString:function(){
if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50);e(e.P+e.F*r(4)(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=i(this),r=o(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},function(t,n,r){var e=r(7)("toPrimitive"),i=Date.prototype;e in i||r(27)(i,e,r(204))},function(t,n,r){var e=Date.prototype,i="Invalid Date",o="toString",u=e[o],c=e.getTime;new Date(NaN)+""!=i&&r(28)(e,o,function(){var t=c.call(this);return t===t?u.call(this):i})},function(t,n,r){var e=r(1);e(e.P,"Function",{bind:r(163)})},function(t,n,r){"use strict";var e=r(6),i=r(32),o=r(7)("hasInstance"),u=Function.prototype;o in u||r(11).f(u,o,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=i(t);)if(this.prototype===t)return!0;return!1}})},function(t,n,r){var e=r(11).f,i=r(66),o=r(24),u=Function.prototype,c="name",f=Object.isExtensible||function(){return!0};c in u||r(10)&&e(u,c,{configurable:!0,get:function(){try{var t=this,n=(""+t).match(/^\s*function ([^ (]*)/)[1];return o(t,c)||!f(t)||e(t,c,i(5,n)),n}catch(t){return""}}})},function(t,n,r){var e=r(1),i=r(171),o=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:i(t-1+o(t-1)*o(t+1))}})},function(t,n,r){function e(t){return isFinite(t=+t)&&0!=t?t<0?-e(-t):Math.log(t+Math.sqrt(t*t+1)):t}var i=r(1),o=Math.asinh;i(i.S+i.F*!(o&&1/o(0)>0),"Math",{asinh:e})},function(t,n,r){var e=r(1),i=Math.atanh;e(e.S+e.F*!(i&&1/i(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},function(t,n,r){var e=r(1),i=r(142);e(e.S,"Math",{cbrt:function(t){return i(t=+t)*Math.pow(Math.abs(t),1/3)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},function(t,n,r){var e=r(1),i=Math.exp;e(e.S,"Math",{cosh:function(t){return(i(t=+t)+i(-t))/2}})},function(t,n,r){var e=r(1),i=r(141);e(e.S+e.F*(i!=Math.expm1),"Math",{expm1:i})},function(t,n,r){var e=r(1),i=r(142),o=Math.pow,u=o(2,-52),c=o(2,-23),f=o(2,127)*(2-c),a=o(2,-126),s=function(t){return t+1/u-1/u};e(e.S,"Math",{fround:function(t){var n,r,e=Math.abs(t),o=i(t);return e<a?o*s(e/a/c)*a*c:(n=(1+c/u)*e,r=n-(n-e),r>f||r!=r?o*(1/0):o*r)}})},function(t,n,r){var e=r(1),i=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,o=0,u=0,c=arguments.length,f=0;u<c;)r=i(arguments[u++]),f<r?(e=f/r,o=o*e*e+1,f=r):r>0?(e=r/f,o+=e*e):o+=r;return f===1/0?1/0:f*Math.sqrt(o)}})},function(t,n,r){var e=r(1),i=Math.imul;e(e.S+e.F*r(4)(function(){return-5!=i(4294967295,5)||2!=i.length}),"Math",{imul:function(t,n){var r=65535,e=+t,i=+n,o=r&e,u=r&i;return 0|o*u+((r&e>>>16)*u+o*(r&i>>>16)<<16>>>0)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log10:function(t){return Math.log(t)/Math.LN10}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log1p:r(171)})},function(t,n,r){var e=r(1);e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},function(t,n,r){var e=r(1);e(e.S,"Math",{sign:r(142)})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S+e.F*r(4)(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(i(t)-i(-t))/2:(o(t-1)-o(-t-1))*(Math.E/2)}})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S,"Math",{tanh:function(t){var n=i(t=+t),r=i(-t);return n==1/0?1:r==1/0?-1:(n-r)/(o(t)+o(-t))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(45),u=r(136),c=r(50),f=r(4),a=r(71).f,s=r(31).f,l=r(11).f,h=r(82).trim,v="Number",p=e[v],d=p,y=p.prototype,g=o(r(70)(y))==v,b="trim"in String.prototype,m=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){n=b?n.trim():h(n,3);var r,e,i,o=n.charCodeAt(0);if(43===o||45===o){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===o){switch(n.charCodeAt(1)){case 66:case 98:e=2,i=49;break;case 79:case 111:e=8,i=55;break;default:return+n}for(var u,f=n.slice(2),a=0,s=f.length;a<s;a++)if((u=f.charCodeAt(a))<48||u>i)return NaN;return parseInt(f,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(g?f(function(){y.valueOf.call(r)}):o(r)!=v)?u(new d(m(n)),r,p):m(n)};for(var x,w=r(10)?a(d):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),S=0;w.length>S;S++)i(d,x=w[S])&&!i(p,x)&&l(p,x,s(d,x));p.prototype=y,y.constructor=p,r(28)(e,v,p)}},function(t,n,r){var e=r(1);e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},function(t,n,r){var e=r(1),i=r(3).isFinite;e(e.S,"Number",{isFinite:function(t){return"number"==typeof t&&i(t)}})},function(t,n,r){var e=r(1);e(e.S,"Number",{isInteger:r(168)})},function(t,n,r){var e=r(1);e(e.S,"Number",{isNaN:function(t){return t!=t}})},function(t,n,r){var e=r(1),i=r(168),o=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return i(t)&&o(t)<=9007199254740991}})},function(t,n,r){var e=r(1);e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},function(t,n,r){var e=r(1);e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},function(t,n,r){var e=r(1),i=r(178);e(e.S+e.F*(Number.parseFloat!=i),"Number",{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.S+e.F*(Number.parseInt!=i),"Number",{parseInt:i})},function(t,n,r){"use strict";var e=r(1),i=r(67),o=r(159),u=r(149),c=1..toFixed,f=Math.floor,a=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l="0",h=function(t,n){for(var r=-1,e=n;++r<6;)e+=t*a[r],a[r]=e%1e7,e=f(e/1e7)},v=function(t){for(var n=6,r=0;--n>=0;)r+=a[n],a[n]=f(r/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==a[t]){var r=String(a[t]);n=""===n?r:n+u.call(l,7-r.length)+r}return n},d=function(t,n,r){return 0===n?r:n%2==1?d(t,n-1,r*t):d(t*t,n/2,r)},y=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r(4)(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,f=o(this,s),a=i(t),g="",b=l;if(a<0||a>20)throw RangeError(s);if(f!=f)return"NaN";if(f<=-1e21||f>=1e21)return String(f);if(f<0&&(g="-",f=-f),f>1e-21)if(n=y(f*d(2,69,1))-69,r=n<0?f*d(2,-n,1):f/d(2,n,1),r*=4503599627370496,(n=52-n)>0){for(h(0,r),e=a;e>=7;)h(1e7,0),e-=7;for(h(d(10,e,1),0),e=n-1;e>=23;)v(1<<23),e-=23;v(1<<e),h(1,1),v(2),b=p()}else h(0,r),h(1<<-n,0),b=p()+u.call(l,a);return a>0?(c=b.length,b=g+(c<=a?"0."+u.call(l,a-c)+b:b.slice(0,c-a)+"."+b.slice(c-a))):b=g+b,b}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=r(159),u=1..toPrecision;e(e.P+e.F*(i(function(){return"1"!==u.call(1,void 0)})||!i(function(){u.call({})})),"Number",{toPrecision:function(t){var n=o(this,"Number#toPrecision: incorrect invocation!");return void 0===t?u.call(n):u.call(n,t)}})},function(t,n,r){var e=r(1);e(e.S+e.F,"Object",{assign:r(172)})},function(t,n,r){var e=r(1);e(e.S,"Object",{create:r(70)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperties:r(173)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperty:r(11).f})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("freeze",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(30),i=r(31).f;r(49)("getOwnPropertyDescriptor",function(){return function(t,n){return i(e(t),n)}})},function(t,n,r){r(49)("getOwnPropertyNames",function(){return r(174).f})},function(t,n,r){var e=r(17),i=r(32);r(49)("getPrototypeOf",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6);r(49)("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},function(t,n,r){var e=r(6);r(49)("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(6);r(49)("isSealed",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(1);e(e.S,"Object",{is:r(180)})},function(t,n,r){var e=r(17),i=r(72);r(49)("keys",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("preventExtensions",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("seal",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(1);e(e.S,"Object",{setPrototypeOf:r(144).set})},function(t,n,r){"use strict";var e=r(114),i={};i[r(7)("toStringTag")]="z",i+""!="[object z]"&&r(28)(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},function(t,n,r){var e=r(1),i=r(178);e(e.G+e.F*(parseFloat!=i),{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.G+e.F*(parseInt!=i),{parseInt:i})},function(t,n,r){"use strict";var e,i,o,u=r(69),c=r(3),f=r(53),a=r(114),s=r(1),l=r(6),h=r(26),v=r(68),p=r(79),d=r(146),y=r(151).set,g=r(143)(),b="Promise",m=c.TypeError,x=c.process,w=c[b],x=c.process,S="process"==a(x),_=function(){},O=!!function(){try{var t=w.resolve(1),n=(t.constructor={})[r(7)("species")]=function(t){t(_,_)};return(S||"function"==typeof PromiseRejectionEvent)&&t.then(_)instanceof n}catch(t){}}(),E=function(t,n){return t===n||t===w&&n===o},P=function(t){var n;return!(!l(t)||"function"!=typeof(n=t.then))&&n},j=function(t){return E(w,t)?new F(t):new i(t)},F=i=function(t){var n,r;this.promise=new t(function(t,e){if(void 0!==n||void 0!==r)throw m("Bad Promise constructor");n=t,r=e}),this.resolve=h(n),this.reject=h(r)},M=function(t){try{t()}catch(t){return{error:t}}},A=function(t,n){if(!t._n){t._n=!0;var r=t._c;g(function(){for(var e=t._v,i=1==t._s,o=0;r.length>o;)!function(n){var r,o,u=i?n.ok:n.fail,c=n.resolve,f=n.reject,a=n.domain;try{u?(i||(2==t._h&&I(t),t._h=1),!0===u?r=e:(a&&a.enter(),r=u(e),a&&a.exit()),r===n.promise?f(m("Promise-chain cycle")):(o=P(r))?o.call(r,c,f):c(r)):f(e)}catch(t){f(t)}}(r[o++]);t._c=[],t._n=!1,n&&!t._h&&N(t)})}},N=function(t){y.call(c,function(){var n,r,e,i=t._v;if(T(t)&&(n=M(function(){S?x.emit("unhandledRejection",i,t):(r=c.onunhandledrejection)?r({promise:t,reason:i}):(e=c.console)&&e.error&&e.error("Unhandled promise rejection",i)}),t._h=S||T(t)?2:1),t._a=void 0,n)throw n.error})},T=function(t){if(1==t._h)return!1;for(var n,r=t._a||t._c,e=0;r.length>e;)if(n=r[e++],n.fail||!T(n.promise))return!1;return!0},I=function(t){y.call(c,function(){var n;S?x.emit("rejectionHandled",t):(n=c.onrejectionhandled)&&n({promise:t,reason:t._v})})},k=function(t){var n=this;n._d||(n._d=!0,n=n._w||n,n._v=t,n._s=2,n._a||(n._a=n._c.slice()),A(n,!0))},L=function(t){var n,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===t)throw m("Promise can't be resolved itself");(n=P(t))?g(function(){var e={_w:r,_d:!1};try{n.call(t,f(L,e,1),f(k,e,1))}catch(t){k.call(e,t)}}):(r._v=t,r._s=1,A(r,!1))}catch(t){k.call({_w:r,_d:!1},t)}}};O||(w=function(t){v(this,w,b,"_h"),h(t),e.call(this);try{t(f(L,this,1),f(k,this,1))}catch(t){k.call(this,t)}},e=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},e.prototype=r(73)(w.prototype,{then:function(t,n){var r=j(d(this,w));return r.ok="function"!=typeof t||t,r.fail="function"==typeof n&&n,r.domain=S?x.domain:void 0,this._c.push(r),this._a&&this._a.push(r),this._s&&A(this,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),F=function(){var t=new e;this.promise=t,this.resolve=f(L,t,1),this.reject=f(k,t,1)}),s(s.G+s.W+s.F*!O,{Promise:w}),r(81)(w,b),r(74)(b),o=r(52)[b],s(s.S+s.F*!O,b,{reject:function(t){var n=j(this);return(0,n.reject)(t),n.promise}}),s(s.S+s.F*(u||!O),b,{resolve:function(t){if(t instanceof w&&E(t.constructor,this))return t;var n=j(this);return(0,n.resolve)(t),n.promise}}),s(s.S+s.F*!(O&&r(123)(function(t){w.all(t).catch(_)})),b,{all:function(t){var n=this,r=j(n),e=r.resolve,i=r.reject,o=M(function(){var r=[],o=0,u=1;p(t,!1,function(t){var c=o++,f=!1;r.push(void 0),u++,n.resolve(t).then(function(t){f||(f=!0,r[c]=t,--u||e(r))},i)}),--u||e(r)});return o&&i(o.error),r.promise},race:function(t){var n=this,r=j(n),e=r.reject,i=M(function(){p(t,!1,function(t){n.resolve(t).then(r.resolve,e)})});return i&&e(i.error),r.promise}})},function(t,n,r){var e=r(1),i=r(26),o=r(2),u=(r(3).Reflect||{}).apply,c=Function.apply;e(e.S+e.F*!r(4)(function(){u(function(){})}),"Reflect",{apply:function(t,n,r){var e=i(t),f=o(r);return u?u(e,n,f):c.call(e,n,f)}})},function(t,n,r){var e=r(1),i=r(70),o=r(26),u=r(2),c=r(6),f=r(4),a=r(163),s=(r(3).Reflect||{}).construct,l=f(function(){function t(){}return!(s(function(){},[],t)instanceof t)}),h=!f(function(){s(function(){})});e(e.S+e.F*(l||h),"Reflect",{construct:function(t,n){o(t),u(n);var r=arguments.length<3?t:o(arguments[2]);if(h&&!l)return s(t,n,r);if(t==r){switch(n.length){case 0:return new t;case 1:return new t(n[0]);case 2:return new t(n[0],n[1]);case 3:return new t(n[0],n[1],n[2]);case 4:return new t(n[0],n[1],n[2],n[3])}var e=[null];return e.push.apply(e,n),new(a.apply(t,e))}var f=r.prototype,v=i(c(f)?f:Object.prototype),p=Function.apply.call(t,v,n);return c(p)?p:v}})},function(t,n,r){var e=r(11),i=r(1),o=r(2),u=r(50);i(i.S+i.F*r(4)(function(){Reflect.defineProperty(e.f({},1,{value:1}),1,{value:2})}),"Reflect",{defineProperty:function(t,n,r){o(t),n=u(n,!0),o(r);try{return e.f(t,n,r),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(31).f,o=r(2);e(e.S,"Reflect",{deleteProperty:function(t,n){var r=i(o(t),n);return!(r&&!r.configurable)&&delete t[n]}})},function(t,n,r){"use strict";var e=r(1),i=r(2),o=function(t){this._t=i(t),this._i=0;var n,r=this._k=[];for(n in t)r.push(n)};r(139)(o,"Object",function(){var t,n=this,r=n._k;do{if(n._i>=r.length)return{value:void 0,done:!0}}while(!((t=r[n._i++])in n._t));return{value:t,done:!1}}),e(e.S,"Reflect",{enumerate:function(t){return new o(t)}})},function(t,n,r){var e=r(31),i=r(1),o=r(2);i(i.S,"Reflect",{getOwnPropertyDescriptor:function(t,n){return e.f(o(t),n)}})},function(t,n,r){var e=r(1),i=r(32),o=r(2);e(e.S,"Reflect",{getPrototypeOf:function(t){return i(o(t))}})},function(t,n,r){function e(t,n){var r,c,s=arguments.length<3?t:arguments[2];return a(t)===s?t[n]:(r=i.f(t,n))?u(r,"value")?r.value:void 0!==r.get?r.get.call(s):void 0:f(c=o(t))?e(c,n,s):void 0}var i=r(31),o=r(32),u=r(24),c=r(1),f=r(6),a=r(2);c(c.S,"Reflect",{get:e})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{has:function(t,n){return n in t}})},function(t,n,r){var e=r(1),i=r(2),o=Object.isExtensible;e(e.S,"Reflect",{isExtensible:function(t){return i(t),!o||o(t)}})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{ownKeys:r(177)})},function(t,n,r){var e=r(1),i=r(2),o=Object.preventExtensions;e(e.S,"Reflect",{preventExtensions:function(t){i(t);try{return o&&o(t),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(144);i&&e(e.S,"Reflect",{setPrototypeOf:function(t,n){i.check(t,n);try{return i.set(t,n),!0}catch(t){return!1}}})},function(t,n,r){function e(t,n,r){var f,h,v=arguments.length<4?t:arguments[3],p=o.f(s(t),n);if(!p){if(l(h=u(t)))return e(h,n,r,v);p=a(0)}return c(p,"value")?!(!1===p.writable||!l(v)||(f=o.f(v,n)||a(0),f.value=r,i.f(v,n,f),0)):void 0!==p.set&&(p.set.call(v,r),!0)}var i=r(11),o=r(31),u=r(32),c=r(24),f=r(1),a=r(66),s=r(2),l=r(6);f(f.S,"Reflect",{set:e})},function(t,n,r){var e=r(3),i=r(136),o=r(11).f,u=r(71).f,c=r(122),f=r(120),a=e.RegExp,s=a,l=a.prototype,h=/a/g,v=/a/g,p=new a(h)!==h;if(r(10)&&(!p||r(4)(function(){return v[r(7)("match")]=!1,a(h)!=h||a(v)==v||"/a/i"!=a(h,"i")}))){a=function(t,n){var r=this instanceof a,e=c(t),o=void 0===n;return!r&&e&&t.constructor===a&&o?t:i(p?new s(e&&!o?t.source:t,n):s((e=t instanceof a)?t.source:t,e&&o?f.call(t):n),r?this:l,a)};for(var d=u(s),y=0;d.length>y;)!function(t){t in a||o(a,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})}(d[y++]);l.constructor=a,a.prototype=l,r(28)(e,"RegExp",a)}r(74)("RegExp")},function(t,n,r){r(119)("match",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("replace",2,function(t,n,r){return[function(e,i){"use strict";var o=t(this),u=void 0==e?void 0:e[n];return void 0!==u?u.call(e,o,i):r.call(String(o),e,i)},r]})},function(t,n,r){r(119)("search",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("split",2,function(t,n,e){"use strict";var i=r(122),o=e,u=[].push,c="split",f="length",a="lastIndex";if("c"=="abbc"[c](/(b)*/)[1]||4!="test"[c](/(?:)/,-1)[f]||2!="ab"[c](/(?:ab)*/)[f]||4!="."[c](/(.?)(.?)/)[f]||"."[c](/()()/)[f]>1||""[c](/.?/)[f]){var s=void 0===/()??/.exec("")[1];e=function(t,n){var r=String(this);if(void 0===t&&0===n)return[];if(!i(t))return o.call(r,t,n);var e,c,l,h,v,p=[],d=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),y=0,g=void 0===n?4294967295:n>>>0,b=new RegExp(t.source,d+"g");for(s||(e=new RegExp("^"+b.source+"$(?!\\s)",d));(c=b.exec(r))&&!((l=c.index+c[0][f])>y&&(p.push(r.slice(y,c.index)),!s&&c[f]>1&&c[0].replace(e,function(){for(v=1;v<arguments[f]-2;v++)void 0===arguments[v]&&(c[v]=void 0)}),c[f]>1&&c.index<r[f]&&u.apply(p,c.slice(1)),h=c[0][f],y=l,p[f]>=g));)b[a]===c.index&&b[a]++;return y===r[f]?!h&&b.test("")||p.push(""):p.push(r.slice(y)),p[f]>g?p.slice(0,g):p}}else"0"[c](void 0,0)[f]&&(e=function(t,n){return void 0===t&&0===n?[]:o.call(this,t,n)});return[function(r,i){var o=t(this),u=void 0==r?void 0:r[n];return void 0!==u?u.call(r,o,i):e.call(String(o),r,i)},e]})},function(t,n,r){"use strict";r(184);var e=r(2),i=r(120),o=r(10),u="toString",c=/./[u],f=function(t){r(28)(RegExp.prototype,u,t,!0)};r(4)(function(){return"/a/b"!=c.call({source:"a",flags:"b"})})?f(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):void 0)}):c.name!=u&&f(function(){return c.call(this)})},function(t,n,r){"use strict";r(29)("anchor",function(t){return function(n){return t(this,"a","name",n)}})},function(t,n,r){"use strict";r(29)("big",function(t){return function(){return t(this,"big","","")}})},function(t,n,r){"use strict";r(29)("blink",function(t){return function(){return t(this,"blink","","")}})},function(t,n,r){"use strict";r(29)("bold",function(t){return function(){return t(this,"b","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!1);e(e.P,"String",{codePointAt:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="endsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{endsWith:function(t){var n=o(this,t,u),r=arguments.length>1?arguments[1]:void 0,e=i(n.length),f=void 0===r?e:Math.min(i(r),e),a=String(t);return c?c.call(n,a,f):n.slice(f-a.length,f)===a}})},function(t,n,r){"use strict";r(29)("fixed",function(t){return function(){return t(this,"tt","","")}})},function(t,n,r){"use strict";r(29)("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},function(t,n,r){"use strict";r(29)("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},function(t,n,r){var e=r(1),i=r(75),o=String.fromCharCode,u=String.fromCodePoint;e(e.S+e.F*(!!u&&1!=u.length),"String",{fromCodePoint:function(t){for(var n,r=[],e=arguments.length,u=0;e>u;){if(n=+arguments[u++],i(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?o(n):o(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},function(t,n,r){"use strict";var e=r(1),i=r(148),o="includes";e(e.P+e.F*r(134)(o),"String",{includes:function(t){return!!~i(this,t,o).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},function(t,n,r){"use strict";r(29)("italics",function(t){return function(){return t(this,"i","","")}})},function(t,n,r){"use strict";var e=r(147)(!0);r(140)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";r(29)("link",function(t){return function(n){return t(this,"a","href",n)}})},function(t,n,r){var e=r(1),i=r(30),o=r(16);e(e.S,"String",{raw:function(t){for(var n=i(t.raw),r=o(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c<e&&u.push(String(arguments[c]));return u.join("")}})},function(t,n,r){var e=r(1);e(e.P,"String",{repeat:r(149)})},function(t,n,r){"use strict";r(29)("small",function(t){return function(){return t(this,"small","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="startsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{startsWith:function(t){var n=o(this,t,u),r=i(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return c?c.call(n,e,r):n.slice(r,r+e.length)===e}})},function(t,n,r){"use strict";r(29)("strike",function(t){return function(){return t(this,"strike","","")}})},function(t,n,r){"use strict";r(29)("sub",function(t){return function(){return t(this,"sub","","")}})},function(t,n,r){"use strict";r(29)("sup",function(t){return function(){return t(this,"sup","","")}})},function(t,n,r){"use strict";r(82)("trim",function(t){return function(){return t(this,3)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(10),u=r(1),c=r(28),f=r(65).KEY,a=r(4),s=r(126),l=r(81),h=r(76),v=r(7),p=r(182),d=r(153),y=r(206),g=r(205),b=r(138),m=r(2),x=r(30),w=r(50),S=r(66),_=r(70),O=r(174),E=r(31),P=r(11),j=r(72),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(71).f=O.f=Z,r(116).f=X,r(125).f=tt,o&&!r(69)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(27)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){"use strict";var e=r(1),i=r(127),o=r(152),u=r(2),c=r(75),f=r(16),a=r(6),s=r(3).ArrayBuffer,l=r(146),h=o.ArrayBuffer,v=o.DataView,p=i.ABV&&s.isView,d=h.prototype.slice,y=i.VIEW,g="ArrayBuffer";e(e.G+e.W+e.F*(s!==h),{ArrayBuffer:h}),e(e.S+e.F*!i.CONSTR,g,{isView:function(t){return p&&p(t)||a(t)&&y in t}}),e(e.P+e.U+e.F*r(4)(function(){return!new h(2).slice(1,void 0).byteLength}),g,{slice:function(t,n){if(void 0!==d&&void 0===n)return d.call(u(this),t);for(var r=u(this).byteLength,e=c(t,r),i=c(void 0===n?r:n,r),o=new(l(this,h))(f(i-e)),a=new v(this),s=new v(o),p=0;e<i;)s.setUint8(p++,a.getUint8(e++));return o}}),r(74)(g)},function(t,n,r){var e=r(1);e(e.G+e.W+e.F*!r(127).ABV,{DataView:r(152).DataView})},function(t,n,r){r(55)("Float32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Float64",8,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}},!0)},function(t,n,r){"use strict";var e=r(166);r(118)("WeakSet",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t,!0)}},e,!1,!0)},function(t,n,r){"use strict";var e=r(1),i=r(117)(!0);e(e.P,"Array",{includes:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)("includes")},function(t,n,r){var e=r(1),i=r(143)(),o=r(3).process,u="process"==r(45)(o);e(e.G,{asap:function(t){var n=u&&o.domain;i(n?n.bind(t):t)}})},function(t,n,r){var e=r(1),i=r(45);e(e.S,"Error",{isError:function(t){return"Error"===i(t)}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Map",{toJSON:r(165)("Map")})},function(t,n,r){var e=r(1);e(e.S,"Math",{iaddh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o+(e>>>0)+((i&u|(i|u)&~(i+u>>>0))>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{imulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>16,f=i>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>16)+((o*f>>>0)+(a&r)>>16)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{isubh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o-(e>>>0)-((~i&u|~(i^u)&i-u>>>0)>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{umulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>>16,f=i>>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>>16)+((o*f>>>0)+(a&r)>>>16)}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineGetter__:function(t,n){u.f(i(this),t,{get:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineSetter__:function(t,n){u.f(i(this),t,{set:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){var e=r(1),i=r(176)(!0);e(e.S,"Object",{entries:function(t){return i(t)}})},function(t,n,r){var e=r(1),i=r(177),o=r(30),u=r(31),c=r(131);e(e.S,"Object",{getOwnPropertyDescriptors:function(t){for(var n,r=o(t),e=u.f,f=i(r),a={},s=0;f.length>s;)c(a,n=f[s++],e(r,n));return a}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupGetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.get}while(r=u(r))}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupSetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.set}while(r=u(r))}})},function(t,n,r){var e=r(1),i=r(176)(!1);e(e.S,"Object",{values:function(t){return i(t)}})},function(t,n,r){"use strict";var e=r(1),i=r(3),o=r(52),u=r(143)(),c=r(7)("observable"),f=r(26),a=r(2),s=r(68),l=r(73),h=r(27),v=r(79),p=v.RETURN,d=function(t){return null==t?void 0:f(t)},y=function(t){var n=t._c;n&&(t._c=void 0,n())},g=function(t){return void 0===t._o},b=function(t){g(t)||(t._o=void 0,y(t))},m=function(t,n){a(t),this._c=void 0,this._o=t,t=new x(this);try{var r=n(t),e=r;null!=r&&("function"==typeof r.unsubscribe?r=function(){e.unsubscribe()}:f(r),this._c=r)}catch(n){return void t.error(n)}g(this)&&y(this)};m.prototype=l({},{unsubscribe:function(){b(this)}});var x=function(t){this._s=t};x.prototype=l({},{next:function(t){var n=this._s;if(!g(n)){var r=n._o;try{var e=d(r.next);if(e)return e.call(r,t)}catch(t){try{b(n)}finally{throw t}}}},error:function(t){var n=this._s;if(g(n))throw t;var r=n._o;n._o=void 0;try{var e=d(r.error);if(!e)throw t;t=e.call(r,t)}catch(t){try{y(n)}finally{throw t}}return y(n),t},complete:function(t){var n=this._s;if(!g(n)){var r=n._o;n._o=void 0;try{var e=d(r.complete);t=e?e.call(r,t):void 0}catch(t){try{y(n)}finally{throw t}}return y(n),t}}});var w=function(t){s(this,w,"Observable","_f")._f=f(t)};l(w.prototype,{subscribe:function(t){return new m(t,this._f)},forEach:function(t){var n=this;return new(o.Promise||i.Promise)(function(r,e){f(t);var i=n.subscribe({next:function(n){try{return t(n)}catch(t){e(t),i.unsubscribe()}},error:e,complete:r})})}}),l(w,{from:function(t){var n="function"==typeof this?this:w,r=d(a(t)[c]);if(r){var e=a(r.call(t));return e.constructor===n?e:new n(function(t){return e.subscribe(t)})}return new n(function(n){var r=!1;return u(function(){if(!r){try{if(v(t,!1,function(t){if(n.next(t),r)return p})===p)return}catch(t){if(r)throw t;return void n.error(t)}n.complete()}}),function(){r=!0}})},of:function(){for(var t=0,n=arguments.length,r=Array(n);t<n;)r[t]=arguments[t++];return new("function"==typeof this?this:w)(function(t){var n=!1;return u(function(){if(!n){for(var e=0;e<r.length;++e)if(t.next(r[e]),n)return;t.complete()}}),function(){n=!0}})}}),h(w.prototype,c,function(){return this}),e(e.G,{Observable:w}),r(74)("Observable")},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.set;e.exp({defineMetadata:function(t,n,r,e){u(t,n,i(r),o(e))}})},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.map,c=e.store;e.exp({deleteMetadata:function(t,n){var r=arguments.length<3?void 0:o(arguments[2]),e=u(i(n),r,!1);if(void 0===e||!e.delete(t))return!1;if(e.size)return!0;var f=c.get(n);return f.delete(r),!!f.size||c.delete(n)}})},function(t,n,r){var e=r(185),i=r(161),o=r(54),u=r(2),c=r(32),f=o.keys,a=o.key,s=function(t,n){var r=f(t,n),o=c(t);if(null===o)return r;var u=s(o,n);return u.length?r.length?i(new e(r.concat(u))):u:r};o.exp({getMetadataKeys:function(t){return s(u(t),arguments.length<2?void 0:a(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.get,f=e.key,a=function(t,n,r){if(u(t,n,r))return c(t,n,r);var e=o(n);return null!==e?a(t,e,r):void 0};e.exp({getMetadata:function(t,n){return a(t,i(n),arguments.length<3?void 0:f(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.keys,u=e.key;e.exp({getOwnMetadataKeys:function(t){
return o(i(t),arguments.length<2?void 0:u(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.get,u=e.key;e.exp({getOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.key,f=function(t,n,r){if(u(t,n,r))return!0;var e=o(n);return null!==e&&f(t,e,r)};e.exp({hasMetadata:function(t,n){return f(t,i(n),arguments.length<3?void 0:c(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.has,u=e.key;e.exp({hasOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(26),u=e.key,c=e.set;e.exp({metadata:function(t,n){return function(r,e){c(t,n,(void 0!==e?i:o)(r),u(e))}}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Set",{toJSON:r(165)("Set")})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!0);e(e.P,"String",{at:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(46),o=r(16),u=r(122),c=r(120),f=RegExp.prototype,a=function(t,n){this._r=t,this._s=n};r(139)(a,"RegExp String",function(){var t=this._r.exec(this._s);return{value:t,done:null===t}}),e(e.P,"String",{matchAll:function(t){if(i(this),!u(t))throw TypeError(t+" is not a regexp!");var n=String(this),r="flags"in f?String(t.flags):c.call(t),e=new RegExp(t.source,~r.indexOf("g")?r:"g"+r);return e.lastIndex=o(t.lastIndex),new a(e,n)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padEnd:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padStart:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!0)}})},function(t,n,r){"use strict";r(82)("trimLeft",function(t){return function(){return t(this,1)}},"trimStart")},function(t,n,r){"use strict";r(82)("trimRight",function(t){return function(){return t(this,2)}},"trimEnd")},function(t,n,r){r(153)("asyncIterator")},function(t,n,r){r(153)("observable")},function(t,n,r){var e=r(1);e(e.S,"System",{global:r(3)})},function(t,n,r){for(var e=r(155),i=r(28),o=r(3),u=r(27),c=r(80),f=r(7),a=f("iterator"),s=f("toStringTag"),l=c.Array,h=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],v=0;v<5;v++){var p,d=h[v],y=o[d],g=y&&y.prototype;if(g){g[a]||u(g,a,l),g[s]||u(g,s,d),c[d]=l;for(p in e)g[p]||i(g,p,e[p],!0)}}},function(t,n,r){var e=r(1),i=r(151);e(e.G+e.B,{setImmediate:i.set,clearImmediate:i.clear})},function(t,n,r){var e=r(3),i=r(1),o=r(121),u=r(207),c=e.navigator,f=!!c&&/MSIE .\./.test(c.userAgent),a=function(t){return f?function(n,r){return t(o(u,[].slice.call(arguments,2),"function"==typeof n?n:Function(n)),r)}:t};i(i.G+i.B+i.F*f,{setTimeout:a(e.setTimeout),setInterval:a(e.setInterval)})},function(t,n,r){r(330),r(269),r(271),r(270),r(273),r(275),r(280),r(274),r(272),r(282),r(281),r(277),r(278),r(276),r(268),r(279),r(283),r(284),r(236),r(238),r(237),r(286),r(285),r(256),r(266),r(267),r(257),r(258),r(259),r(260),r(261),r(262),r(263),r(264),r(265),r(239),r(240),r(241),r(242),r(243),r(244),r(245),r(246),r(247),r(248),r(249),r(250),r(251),r(252),r(253),r(254),r(255),r(317),r(322),r(329),r(320),r(312),r(313),r(318),r(323),r(325),r(308),r(309),r(310),r(311),r(314),r(315),r(316),r(319),r(321),r(324),r(326),r(327),r(328),r(231),r(233),r(232),r(235),r(234),r(220),r(218),r(224),r(221),r(227),r(229),r(217),r(223),r(214),r(228),r(212),r(226),r(225),r(219),r(222),r(211),r(213),r(216),r(215),r(230),r(155),r(302),r(307),r(184),r(303),r(304),r(305),r(306),r(287),r(183),r(185),r(186),r(342),r(331),r(332),r(337),r(340),r(341),r(335),r(338),r(336),r(339),r(333),r(334),r(288),r(289),r(290),r(291),r(292),r(295),r(293),r(294),r(296),r(297),r(298),r(299),r(301),r(300),r(343),r(369),r(372),r(371),r(373),r(374),r(370),r(375),r(376),r(354),r(357),r(353),r(351),r(352),r(355),r(356),r(346),r(368),r(377),r(345),r(347),r(349),r(348),r(350),r(359),r(360),r(362),r(361),r(364),r(363),r(365),r(366),r(367),r(344),r(358),r(380),r(379),r(378),t.exports=r(52)},function(t,n){function r(t,n){if("string"==typeof n)return t.insertAdjacentHTML("afterend",n);var r=t.nextSibling;return r?t.parentNode.insertBefore(n,r):t.parentNode.appendChild(n)}t.exports=r},,,,,,,,,function(t,n,r){(function(n,r){!function(n){"use strict";function e(t,n,r,e){var i=n&&n.prototype instanceof o?n:o,u=Object.create(i.prototype),c=new p(e||[]);return u._invoke=s(t,r,c),u}function i(t,n,r){try{return{type:"normal",arg:t.call(n,r)}}catch(t){return{type:"throw",arg:t}}}function o(){}function u(){}function c(){}function f(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function a(t){function n(r,e,o,u){var c=i(t[r],t,e);if("throw"!==c.type){var f=c.arg,a=f.value;return a&&"object"==typeof a&&m.call(a,"__await")?Promise.resolve(a.__await).then(function(t){n("next",t,o,u)},function(t){n("throw",t,o,u)}):Promise.resolve(a).then(function(t){f.value=t,o(f)},u)}u(c.arg)}function e(t,r){function e(){return new Promise(function(e,i){n(t,r,e,i)})}return o=o?o.then(e,e):e()}"object"==typeof r&&r.domain&&(n=r.domain.bind(n));var o;this._invoke=e}function s(t,n,r){var e=P;return function(o,u){if(e===F)throw new Error("Generator is already running");if(e===M){if("throw"===o)throw u;return y()}for(r.method=o,r.arg=u;;){var c=r.delegate;if(c){var f=l(c,r);if(f){if(f===A)continue;return f}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(e===P)throw e=M,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);e=F;var a=i(t,n,r);if("normal"===a.type){if(e=r.done?M:j,a.arg===A)continue;return{value:a.arg,done:r.done}}"throw"===a.type&&(e=M,r.method="throw",r.arg=a.arg)}}}function l(t,n){var r=t.iterator[n.method];if(r===g){if(n.delegate=null,"throw"===n.method){if(t.iterator.return&&(n.method="return",n.arg=g,l(t,n),"throw"===n.method))return A;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return A}var e=i(r,t.iterator,n.arg);if("throw"===e.type)return n.method="throw",n.arg=e.arg,n.delegate=null,A;var o=e.arg;return o?o.done?(n[t.resultName]=o.value,n.next=t.nextLoc,"return"!==n.method&&(n.method="next",n.arg=g),n.delegate=null,A):o:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,A)}function h(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function v(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(h,this),this.reset(!0)}function d(t){if(t){var n=t[w];if(n)return n.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,e=function n(){for(;++r<t.length;)if(m.call(t,r))return n.value=t[r],n.done=!1,n;return n.value=g,n.done=!0,n};return e.next=e}}return{next:y}}function y(){return{value:g,done:!0}}var g,b=Object.prototype,m=b.hasOwnProperty,x="function"==typeof Symbol?Symbol:{},w=x.iterator||"@@iterator",S=x.asyncIterator||"@@asyncIterator",_=x.toStringTag||"@@toStringTag",O="object"==typeof t,E=n.regeneratorRuntime;if(E)return void(O&&(t.exports=E));E=n.regeneratorRuntime=O?t.exports:{},E.wrap=e;var P="suspendedStart",j="suspendedYield",F="executing",M="completed",A={},N={};N[w]=function(){return this};var T=Object.getPrototypeOf,I=T&&T(T(d([])));I&&I!==b&&m.call(I,w)&&(N=I);var k=c.prototype=o.prototype=Object.create(N);u.prototype=k.constructor=c,c.constructor=u,c[_]=u.displayName="GeneratorFunction",E.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===u||"GeneratorFunction"===(n.displayName||n.name))},E.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,c):(t.__proto__=c,_ in t||(t[_]="GeneratorFunction")),t.prototype=Object.create(k),t},E.awrap=function(t){return{__await:t}},f(a.prototype),a.prototype[S]=function(){return this},E.AsyncIterator=a,E.async=function(t,n,r,i){var o=new a(e(t,n,r,i));return E.isGeneratorFunction(n)?o:o.next().then(function(t){return t.done?t.value:o.next()})},f(k),k[_]="Generator",k.toString=function(){return"[object Generator]"},E.keys=function(t){var n=[];for(var r in t)n.push(r);return n.reverse(),function r(){for(;n.length;){var e=n.pop();if(e in t)return r.value=e,r.done=!1,r}return r.done=!0,r}},E.values=d,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=g,this.done=!1,this.delegate=null,this.method="next",this.arg=g,this.tryEntries.forEach(v),!t)for(var n in this)"t"===n.charAt(0)&&m.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=g)},stop:function(){this.done=!0;var t=this.tryEntries[0],n=t.completion;if("throw"===n.type)throw n.arg;return this.rval},dispatchException:function(t){function n(n,e){return o.type="throw",o.arg=t,r.next=n,e&&(r.method="next",r.arg=g),!!e}if(this.done)throw t;for(var r=this,e=this.tryEntries.length-1;e>=0;--e){var i=this.tryEntries[e],o=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var u=m.call(i,"catchLoc"),c=m.call(i,"finallyLoc");if(u&&c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,n){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc<=this.prev&&m.call(e,"finallyLoc")&&this.prev<e.finallyLoc){var i=e;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=n&&n<=i.finallyLoc&&(i=null);var o=i?i.completion:{};return o.type=t,o.arg=n,i?(this.method="next",this.next=i.finallyLoc,A):this.complete(o)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),A},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),v(r),A}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc===t){var e=r.completion;if("throw"===e.type){var i=e.arg;v(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:d(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=g),A}}}("object"==typeof n?n:"object"==typeof window?window:"object"==typeof self?self:this)}).call(n,function(){return this}(),r(158))}])</script><script src="/./main.0cf68a.js"></script><script>!function(){!function(e){var t=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(t),t.setAttribute("src",e)}("/slider.e37972.js")}()</script>


    
<div class="tools-col" q-class="show:isShow,hide:isShow|isFalse" q-on="click:stop(e)">
  <div class="tools-nav header-menu">
    
    
      
      
      
    
      
      
      
    
    

    <ul style="width: 70%">
    
    
      
      <li style="width: 50%" q-on="click: openSlider(e, 'innerArchive')"><a href="javascript:void(0)" q-class="active:innerArchive">所有文章</a></li>
      
        
      
      <li style="width: 50%" q-on="click: openSlider(e, 'aboutme')"><a href="javascript:void(0)" q-class="active:aboutme">关于我</a></li>
      
        
    </ul>
  </div>
  <div class="tools-wrap">
    
    	<section class="tools-section tools-section-all" q-show="innerArchive">
        <div class="search-wrap">
          <input class="search-ipt" q-model="search" type="text" placeholder="find something…">
          <i class="icon-search icon" q-show="search|isEmptyStr"></i>
          <i class="icon-close icon" q-show="search|isNotEmptyStr" q-on="click:clearChose(e)"></i>
        </div>
        <div class="widget tagcloud search-tag">
          <p class="search-tag-wording">tag:</p>
          <label class="search-switch">
            <input type="checkbox" q-on="click:toggleTag(e)" q-attr="checked:showTags">
          </label>
          <ul class="article-tag-list" q-show="showTags">
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">设计模式</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">错误记录</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">工具使用</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">代码整洁</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color1">linux</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">智能指针</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color1">boost</a>
              </li>
            
            <div class="clearfix"></div>
          </ul>
        </div>
        <ul class="search-ul">
          <p q-show="jsonFail" style="padding: 20px; font-size: 12px;">
            缺失模块。<br/>1、请确保node版本大于6.2<br/>2、在博客根目录（注意不是yilia根目录）执行以下命令：<br/> npm i hexo-generator-json-content --save<br/><br/>
            3、在根目录_config.yml里添加配置：
<pre style="font-size: 12px;" q-show="jsonFail">
  jsonContent:
    meta: false
    pages: false
    posts:
      title: true
      date: true
      path: true
      text: false
      raw: false
      content: false
      slug: false
      updated: false
      comments: false
      link: false
      permalink: false
      excerpt: false
      categories: false
      tags: true
</pre>
          </p>
          <li class="search-li" q-repeat="items" q-show="isShow">
            <a q-attr="href:path|urlformat" class="search-title"><i class="icon-quo-left icon"></i><span q-text="title"></span></a>
            <p class="search-time">
              <i class="icon-calendar icon"></i>
              <span q-text="date|dateformat"></span>
            </p>
            <p class="search-tag">
              <i class="icon-price-tags icon"></i>
              <span q-repeat="tags" q-on="click:choseTag(e, name)" q-text="name|tagformat"></span>
            </p>
          </li>
        </ul>
    	</section>
    

    

    
    	<section class="tools-section tools-section-me" q-show="aboutme">
  	  	
  	  		<div class="aboutme-wrap" id="js-aboutme">热爱技术&lt;br&gt;&lt;br&gt;还在努力学习中，千里之行始于足下&lt;br/&gt;&lt;br/&gt;谢谢大家</div>
  	  	
    	</section>
    
  </div>
  
</div>
    <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>
  </div>
</body>
</html>